{"owner":"MarlinFirmware","repo":"Marlin","hasSkills":true,"totalSkillsCount":12,"totalTokensCount":19656,"categories":["anthropic-skill","root-instruction"],"hasMcp":false,"mcpConfig":null,"found":["buildroot/skills/marlin-config-options/SKILL.md","buildroot/skills/marlin-build-test/SKILL.md","buildroot/skills/marlin-code-style/SKILL.md","buildroot/skills/marlin-codebase-navigation/SKILL.md","buildroot/skills/marlin-fork-harvesting/SKILL.md","buildroot/skills/marlin-extui-backends/SKILL.md","buildroot/skills/marlin-hal-work/SKILL.md","buildroot/skills/marlin-pr-workflow/SKILL.md","buildroot/skills/marlin-unit-tests/SKILL.md","AGENTS.md","buildroot/skills/README.md","buildroot/skills/marlin-solving-bugs/SKILL.md"],"skills":{"buildroot/skills/marlin-config-options/SKILL.md":"---\nname: marlin-config-options\ndescription: Use when you need to find, enable, disable, or set Marlin configuration options (Configuration.h / Configuration_adv.h) from the command line. Wraps buildroot/bin/opt_find, opt_enable, opt_disable, opt_set, and opt_add.\n---\n\n# Marlin Configuration Options from the Command Line\n\nThese scripts edit `Marlin/Configuration.h` and `Marlin/Configuration_adv.h` in place. Run them from the repo root. Commit or stash your own edits first so the changes are easy to revert, or run `buildroot/bin/restore_configs` afterward (which does a `git checkout` of the config files, discarding uncommitted edits to them).\n\n## Find\n\n    buildroot/bin/opt_find LIN_ADV         # enabled or disabled options matching the name\n    buildroot/bin/opt_find -e BABYSTEP     # only enabled\n    buildroot/bin/opt_find -d BABYSTEP     # only disabled\n\n## Enable / disable\n\n    buildroot/bin/opt_enable LIN_ADVANCE ADAPTIVE_STEP_SMOOTHING\n    buildroot/bin/opt_disable ADAPTIVE_STEP_SMOOTHING\n\n`opt_enable` exits with an error if an option can't be found.\n\n## Set a value\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_RAMPS_14_EFB EXTRUDERS 2\n\nTakes `<name> <value>` pairs. An option that does not exist is added to `Configuration.h`.\n\n## Use example configurations\n\n    buildroot/bin/use_example_configs Creality/CR-10/CrealityV1\n\nFetches the named example from the Configurations repo (branch matched to your working branch, otherwise `bugfix-2.1.x`). Use `release-2.0.9.4:Creality/CR-10/CrealityV1` to pick a branch or tag.\n\n## Tips\n\n- Option names are matched exactly by enable/disable/set. Use `opt_find` first.\n- Some options need others (see `Marlin/src/inc/SanityCheck.h`). A build error from a sanity check tells you what is missing.\n- The CI test files in `buildroot/tests/` use these same operations. Read a few as examples.\n","buildroot/skills/marlin-build-test/SKILL.md":"---\nname: marlin-build-test\ndescription: Use when you need to compile Marlin for a board, apply a CI test configuration, or run the build tests locally. Wraps buildroot/bin/mftest, mfenvs, run_tests, and the Makefile targets.\n---\n\n# Build and Test Marlin\n\nRequires PlatformIO (`pio`) in your `PATH`. Run everything from the repo root.\n\n## Build the configured board\n\n1. Set `MOTHERBOARD` in `Marlin/Configuration.h` and set up your options.\n2. List the PlatformIO environments for the board: `buildroot/bin/mfenvs`\n3. Build the auto-detected environment: `buildroot/bin/mftest -a` (rebuild with `-r`, silent with `-s`).\n4. Or build one environment directly: `pio run -e <env>`\n\n## Apply a CI test config and build it\n\nTests live in `buildroot/tests/<env>/config-NN.ini` and are applied with the `opt_*` tools.\n\n- Interactive menu: `buildroot/bin/mftest`\n- Specific test: `buildroot/bin/mftest -t mega2560 -n 5 -y` (`-t` env name or shortcut, `-n` test index, `-y` build without asking)\n- Start from default configs first: add `-d`\n- Only emit the config changes without building: add `-i`\n- Use the Docker/make method: add `-m`\n\nShortcuts for `-t`: `tree due esp lin lp8 lp9 m128 mega stm f1 f4 f7 s6 teensy t31 t35 t40`.\n\n## Add tests for a new feature or option\n\nA PR that adds a feature or option should add tests, so CI keeps it building and working.\n\n**Build test** (`buildroot/tests/<env>/config-NN.ini`)\n\n1. Pick the env directory that matches a board and platform that can build the feature. Use `buildroot/bin/mfenvs` or the existing directories under `buildroot/tests`.\n2. Prefer adding the option to an existing `config-NN.ini` with a similar feature set. Add a new file only for a combination that no existing test covers. Use the next free number.\n3. Each file begins with a header (`# Test: <env>/config-NN.ini`, then a one-line description of the main options) and a `[config:base]` section. Start from an example with `ini_use_config = example/...`, or leave it out for the defaults.\n4. Set options as `name = value` or `name = on`. Test the feature enabled along with the options it depends on, and with the dependent sub-options.\n5. Run just your test: `buildroot/bin/mftest -t <env> -n <index> -y` or `make tests-single-local TEST_TARGET=<env> ONLY_TEST=<index>`, where `<index>` is the test's position in file order (1-based) and normally matches `NN`.\n6. Keep the number of tests small. Every test adds to CI time.\n\n**Unit test** (when the logic can be checked without hardware)\n\nUnit tests run natively on the `linux_native_test` environment. Good candidates are parsing, math, conversions, state machines and G-code handling. Anything that needs real pins, timers, or devices isn't a good candidate. See `test/README.md`.\n\n1. Add `Marlin/tests/<area>/test_<name>.cpp`. Copy the license header and the `MARLIN_TEST(group, name)` macros from an existing test such as `Marlin/tests/feature/test_runout.cpp`.\n2. Wrap the file's contents in `#if ENABLED(FEATURE)` so it compiles only for configs that enable it.\n3. If no existing config in `test/*.ini` enables your feature, add one (`test/NNN-name.ini`). It must use `motherboard = BOARD_SIMULATED` and include only the options needed to pass the sanity checks.\n4. Run: `make unit-test-all-local`, or one config with `make unit-test-single-local UNIT_TEST_CONFIG=<name>`. (There is a `-docker` variant of each.)\n\n## Run the tests like CI does\n\n- One environment: `make tests-single-local TEST_TARGET=mega2560`\n- One test of it: `make tests-single-local TEST_TARGET=mega2560 ONLY_TEST=2`\n- Everything (slow): `make tests-all-local`\n- In Docker: `make tests-single-local-docker TEST_TARGET=mega2560` (see `make help`)\n- Unit tests: `make unit-test-all-local`\n\nThe test runner restores the configs between tests and may `git reset --hard` (see `GIT_RESET_HARD` in `make help`), so commit or stash your work first.\n\n## Restore configuration files\n\nAfter testing, run `buildroot/bin/restore_configs`. It deletes generated files (`marlin_config.json`, `_Bootscreen.h`, `_Statusscreen.h`) and does a `git checkout` of the configuration files, discarding any uncommitted edits to them. `restore_configs -d` instead loads the default example configs.\n\n## Choosing what to build\n\n- Build at least one environment each for any HAL or board family you changed.\n- A change to common code deserves an AVR build (tight flash) and a 32-bit build.\n- For features, build with the feature both enabled and disabled.\n- New feature or option: add a build test and, where it makes sense, a unit test (see above).\n\n## Report results\n\nState exactly which envs and tests you built. Do not claim a pass that you did not see.\n","buildroot/skills/marlin-code-style/SKILL.md":"---\nname: marlin-code-style\ndescription: Use before committing C/C++ changes to Marlin, to match project conventions and indentation in new code without reformatting existing code.\n---\n\n# Marlin Code Style\n\nFull standards: https://marlinfw.org/docs/development/coding_standards.html\n\n## Rules of thumb\n\n- Match the file you are editing: indentation (2 spaces, no tabs), brace placement, spacing, naming.\n- Code inside preprocessor blocks (`#if` / `#else` / `#endif`) is indented one level per nesting depth, the same as code inside braces. Copy how nearby code does it.\n- Use `ENABLED(FEATURE)` / `DISABLED(FEATURE)` for options, and `#if HAS_...` for derived capabilities.\n- Keep optional code behind its feature flag so it costs nothing when disabled.\n- Code must build on 8-bit AVR as well as 32-bit targets. Avoid unnecessary `double`, `String`, and heap allocation.\n- Comments should say why, concisely. End every file with a newline. No trailing whitespace.\n\n## Don't reformat existing code\n\n- Existing code may have ad hoc alignment and spacing (aligned columns, grouped one-liners). Leave it alone.\n- Don't re-indent, re-wrap, or re-space lines you aren't otherwise changing. Keep diffs small and focused.\n- Exception: It's good to keep related eol comments aligned.\n- Review `git diff` before committing and revert any whitespace-only churn.\n\n## Auto-formatters\n\nDon't run the auto-formatter on existing files. `buildroot/bin/format_code` and `uncrust` (uncrustify) get close to the Marlin style but don't match it exactly, and will produce unwanted churn. If you use one, apply it only to a scratch copy to see suggestions, then hand-apply what is correct to your own new lines.\n","buildroot/skills/marlin-codebase-navigation/SKILL.md":"---\nname: marlin-codebase-navigation\ndescription: Use when you need to find where something is defined, what uses it, or how a command or setting flows through Marlin's source. Uses only git, grep, and the repo layout, with optional tools as extras.\n---\n\n# Navigate the Marlin Source\n\nMarlin is a large C++ tree where most code is compiled only under certain options. These steps work with nothing but `git` and a shell. Better tools are listed at the end as optional.\n\nFor the layout, singletons, and processing pipeline, read `AGENTS.md` (sections 2 to 4 and 10) first. This skill covers how to search, and where the common things live.\n\n## Where things live\n\n| Looking for | Look in |\n| --- | --- |\n| A G-code command `Mnnn` / `Gnnn` | `git grep -n \"GcodeSuite::M913\"`. Handlers are in `Marlin/src/gcode/<group>/`, and some files hold several (`M911-M914.cpp`). The dispatch `switch` is in `Marlin/src/gcode/gcode.cpp`, and declarations are in `gcode/gcode.h` |\n| What runs each loop | `setup()` and `loop()` in `Marlin/src/MarlinCore.cpp` |\n| Motion, planner, stepper ISR | `Marlin/src/module/` (`motion`, `planner`, `stepper`) |\n| Temperature, PID, MPC | `Marlin/src/module/temperature.cpp` |\n| Optional feature code | `Marlin/src/feature/` and `gcode/feature/` |\n| An option's derived flags (`HAS_*`) | `Marlin/src/inc/Conditionals-*.h` |\n| Why a config combination is rejected | `Marlin/src/inc/SanityCheck.h`; renamed options are in `Changes.h` |\n| A board's pins | `Marlin/src/pins/<family>/pins_<BOARD>.h`; the `#elif MB(...)` chain is in `pins/pins.h`; the ID is in `core/boards.h` |\n| Platform-specific code | `Marlin/src/HAL/<FAMILY>/`, and that folder's `AGENTS.md` |\n| An LCD string | `Marlin/src/lcd/language/language_en.h` (`MSG_*`) |\n| A menu screen | `Marlin/src/lcd/menu/menu_*.cpp` |\n| A serial or touch display | `Marlin/src/lcd/extui/` |\n| Which source folders a build includes | `ini/features.ini` and `ini/*.ini` |\n| A CI test config | `buildroot/tests/<env>/config-NN.ini` |\n\n## Search recipes\n\n    git grep -n \"OPTION_NAME\" -- Marlin                  # every use, including config comments\n    git grep -n \"SOME_OPTION\" -- 'Marlin/src/inc/*'      # where it is derived or validated\n    git grep -nE \"\\bfunction_name\\(\" -- Marlin/src       # calls and definitions\n    git grep -n \"STR_SOMETHING\\|MSG_SOMETHING\" -- Marlin/src/lcd\n    git log -S\"suspicious_string\" --oneline -- Marlin/src   # when it was introduced or removed\n    git log --oneline -20 -- Marlin/src/module/motion.cpp   # recent changes to a file\n    git blame -L 120,160 Marlin/src/module/motion.cpp       # who changed these lines, then read that PR\n\n## Tracing a setting or command\n\n1. Find the option in `Marlin/Configuration.h` or `Configuration_adv.h`.\n2. `git grep` it under `Marlin/src/inc` to see what it enables and what it conflicts with.\n3. `git grep` it under the rest of `Marlin/src` to find the code it gates.\n4. For a G-code, go from the `case` in `gcode/gcode.cpp` to its handler, then follow the calls into `module/`.\n5. If a symbol seems to have no users, check for macro use (`TERN`, `ENABLED`) and for different spellings under other options before deciding it is dead.\n\n## Pitfalls\n\n- **Most code is conditional.** A match inside `#if ENABLED(X)` is not compiled unless `X` is on. Read the enclosing `#if`s, and build with the option on and off.\n- **Macros hide calls.** `SERIAL_ECHO*`, `TERN*`, `LOOP_*`, and `HAL_PATH` includes do not look like ordinary function calls, so search for the macro name too.\n- **Old issues and forks use old paths.** `ultralcd.*` is now `marlinui.*`, and `Conditionals_post.h` was split into the numbered files. Search the current name before applying advice from an old thread.\n- **Line numbers drift.** Quote a symbol and a file, not only a line number, in a PR or comment.\n- **IDE diagnostics can mislead.** Marlin builds with GCC. A Clang-based language server often reports errors on headers that build fine. Trust the build (skill: marlin-build-test).\n\n## Optional tools\n\nNone of these are required, and none replace reading the code.\n\n- `rg` (ripgrep) is faster than `git grep` on the whole tree and handles the same patterns.\n- Your AI tool's own search (for example Claude Code's file and content search, or a code-exploration sub-agent) works well for broad questions such as \"where is homing decided\". Check its answer with `git grep`.\n- A symbol index such as `universal-ctags` helps with jump-to-definition in an editor. Because of the `#if` blocks it can show several definitions of one name.\n- A code-knowledge-graph MCP server (for example codebase-memory-mcp) can locate definitions, line ranges, and function size from a natural-language query. In testing on Marlin, its call-graph results were incomplete: callers came back at file level and macro-dispatched code had no callees. Use it to locate code and `git grep` to find every use. It indexes a snapshot of the checked-out tree, so re-index after switching branches.\n- If you try a language-server plugin, expect noise from the heavy preprocessor use and keep a real build as the final check.\n","buildroot/skills/marlin-fork-harvesting/SKILL.md":"---\nname: marlin-fork-harvesting\ndescription: Use when evaluating a GitHub fork of Marlin (a vendor's or a community member's) and porting a feature, board, or display driver into mainline as a clean, reviewable PR.\n---\n\n# Port a Feature from a Marlin Fork\n\nForks often hold support for hardware that mainline lacks. Their code can't be merged as-is: it is based on an older Marlin, may restructure the HALs, and may not follow the project style. Port the part that belongs in mainline, and port it cleanly.\n\nCheck the fork's license and keep attribution. Say in the PR where the code came from and credit the author.\n\n## 1. Decide whether it is worth porting\n\n| Question | How to check |\n| --- | --- |\n| Is it maintained? | Last push date |\n| Is it really Marlin? | It has `Marlin/src/` |\n| Is it already in mainline? | Search the Marlin PRs and `git log` for the board or feature |\n| Does it add hardware or features mainline lacks? | See step 3 |\n| Is the quality reasonable? | Read two or three key files |\n\nThe vendor's own repo is a secondary source. It is often a frozen snapshot from the product launch, based on an old Marlin, with stale board IDs and an outdated layout. Use it only to cross-check pin assignments and names, not as a port base.\n\n## 2. Add the fork as a remote\n\n    git remote add <author-slug> https://github.com/<Author>/Marlin\n    git fetch <author-slug>\n    git branch -r | grep <author-slug>\n\n## 3. Find only the relevant changes\n\nA fork that diverged long ago shows thousands of changed files, nearly all of it mainline drift. Filter to what matters:\n\n    git log --oneline upstream/bugfix-2.1.x..<slug>/<branch> | wc -l\n    git diff --name-only upstream/bugfix-2.1.x <slug>/<branch> | grep -iE '<machine>|src/pins/|src/HAL/|src/lcd/'\n    git show <slug>/<branch>:Marlin/Configuration.h | grep -E 'MOTHERBOARD|MACHINE_NAME'\n\nCheck each of these:\n\n- **New HAL?** If the board needs an MCU family that mainline doesn't support, decide with the maintainers whether the HAL is part of this PR or a separate one before you write code.\n- **Board entry?** Check `Marlin/src/core/boards.h`. A new board needs a `boards.h` entry, a `pins.h` entry, and a pins file.\n- **UI code?** Large display-driver or UI changes should go in a separate PR from the core feature.\n\n## 4. Port, don't cherry-pick\n\nMake a clean-room port in most cases. Read the fork's code, then write it in mainline's style on a fresh branch:\n\n    git checkout -b <topic> upstream/bugfix-2.1.x\n\nCherry-pick only when the commits are linear, clean, and conflict-free.\n\nNever port a fork's HAL restructuring (deleted platforms, renamed folders, moved files). Take the target board's pins file and the genuinely new driver or feature.\n\n### Rules\n\n1. **Use mainline's names and layout.** A fork's folder names may differ (for example `hc32f46x/` vs mainline `hc32f4/`). Check `ls Marlin/src/pins/` and follow it.\n2. **Board IDs must be verified against `boards.h`.** A fork's ID is often already taken in mainline. Use the next free ID in the right architecture range:\n\n       grep '#define BOARD_' Marlin/src/core/boards.h | awk '{print $3}' | sort -n\n\n   Keep the `boards.h` order the same as the `#elif MB()` order in `pins.h`.\n\n3. **Config defaults** go in the `Conditionals-*.h` layer. `SanityCheck.h` only validates currently-valid combinations.\n4. **Renamed or removed options** get an `#error` in `Marlin/src/inc/Changes.h` that points to the replacement.\n5. **Old G-codes and M-codes** that the port replaces should keep working as shims where reasonable.\n6. **EEPROM data** follows the struct pattern in `settings.cpp`, with a version bump.\n7. **Don't copy old APIs.** Fork code often uses names removed from mainline. Fix each compile error against current headers rather than adding compatibility macros.\n\n### New ExtUI display drivers\n\nSee skill marlin-extui-backends. In particular:\n\n- Add the driver folder to `ini/features.ini`. If it is missing, the whole driver is silently excluded and every ExtUI callback is \"undefined\".\n- Add the new LCD define to the lists in `Conditionals-2-LCD.h`, and add its `SanityCheck.h` entries.\n- Compare the fork's handlers with the current `ui_api.h` (renamed `onMediaMounted`, handlers with new argument types, newer handlers such as `onSurviveInKilled`).\n\n### Per-HAL notes\n\nRead `Marlin/src/HAL/<FAMILY>/AGENTS.md`. For example, a HAL may lack an EEPROM backend the fork relied on (`PersistentStore::*` undefined at link), or need a board-defined crystal frequency.\n\n## 5. Build and test\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_<NAME>\n    buildroot/bin/mftest -a -n1 -y\n    buildroot/bin/restore_configs\n\n- Build with the feature disabled and enabled.\n- Add a test config for new options (skill: marlin-build-test). A test config must satisfy the sanity checks, for example `NOZZLE_AS_PROBE` needs a zero probe offset, and bed leveling with a physical probe needs `Z_SAFE_HOMING`.\n- Say honestly in the PR what you built, and that no one has tested on hardware if that is so.\n\n## 6. Commit and PR\n\nSplit it: core pins and board first, display driver second, extra features after that (skill: marlin-pr-workflow). Credit the fork's author in the PR description.\n","buildroot/skills/marlin-extui-backends/SKILL.md":"---\nname: marlin-extui-backends\ndescription: Use when adding or changing an ExtUI display backend (DGUS, Anycubic, Nextion, Malyan, FTDI EVE, Neptune 3, IA Creality, serial panels) or the ExtUI API in ui_api.h / ui_api.cpp.\n---\n\n# Marlin ExtUI Backends\n\nExtUI (`Marlin/src/lcd/extui/ui_api.{h,cpp}`) is the API used by serial and touch displays. Backends live in subfolders of `Marlin/src/lcd/extui/`. `example/example.cpp` is the template for a new one.\n\n## Rules\n\n1. **Event handlers (`ExtUI::on*()`) have weak, empty defaults in `ui_api.cpp`.** A backend defines only the handlers it uses. Don't add empty `{}` stubs to a backend, and delete them if you see them. A new handler declared in `ui_api.h` gets exactly one weak empty default in the `WEAK_EXTUI` block in `ui_api.cpp`, inside the same `#if` guard as its declaration. No backend needs to change.\n2. **API functions (`get*` / `set*`) are not event handlers.** They get one real implementation in `ui_api.cpp`, next to the matching getter, and are never weak or inside a backend. A setter moved into the backends leaves empty stubs that silently do nothing.\n3. **Keep the `ExtUI` namespace flat.** Don't use the `using namespace Base` override pattern from `language_*.h`: `ui_api.h` already declares every handler in `ExtUI`, and name hiding breaks overloads such as `onUserConfirmRequired` and `onMeshUpdate`.\n4. **Backends keep to their own folder.** Add the folder to `ini/features.ini` (`build_src_filter=+<src/lcd/extui/<folder>>`), or it will be silently excluded and the link fails with every callback undefined.\n5. **Wire the new LCD define in `Conditionals-2-LCD.h`** (the `IS_EXTUI` list, the `LCD_IS_SERIAL_HOST` list if it is a serial display, and the \"only one LCD\" count), and add its checks to `SanityCheck.h`. Copy a similar backend's entries.\n6. **Handlers must match the current `ui_api.h` signatures.** Backends ported from forks often use renamed or removed handlers. Diff against the full callback list in `ui_api.h` before building.\n7. **After a bulk deletion, tidy up.** Remove runs of blank lines, a blank line directly after `#if` or before `#endif`, orphaned comments, and emptied `#if` blocks. Diff against the base and check that the only `+` lines are the ones you intended.\n\n## A panel that speaks G-code (PanelDue and similar)\n\nTreat it as an ExtUI backend that owns `LCD_SERIAL`, with the whole protocol inside the backend. Add it to `LCD_IS_SERIAL_HOST` (which auto-assigns `LCD_SERIAL_PORT`) rather than routing it through `SERIAL_PORT_2`.\n\n- `LCD_SERIAL` is never a G-code source. The queue reads only `NUM_SERIAL` ports, and `SERIAL_ECHO*` never reaches `LCD_SERIAL`. The backend must call `LCD_SERIAL.begin(LCD_BAUDRATE)` in `onStartup()`, read lines in `onIdle()`, and write replies to `LCD_SERIAL` itself, as Malyan does.\n- Answer status queries directly from `onIdle()`, never through the queue. `idle()` keeps running during `G28`, `M109`, and SD prints, so the panel stays connected while Marlin is busy.\n- Run other commands by injection. `queue.inject()` overwrites the single `injected_commands` buffer (64 bytes), so keep your own FIFO. Hand over whole lines only when `queue.injected_commands[0] == 0`. Send a line too long for the buffer with `queue.enqueue_one()` and retry if that fails.\n- Apply temperature, fan, speed, and flow changes with the ExtUI setters, so they take effect even during a blocking `M109`.\n- Buffer replies and drain them with `LCD_SERIAL_TX_BUFFER_FREE()` (`SERIAL_GET_TX_BUFFER_FREE()` on ESP32 and RP2040). A 1 KB reply takes about 180 ms at 57600 baud, which is too long to block `idle()` during a print.\n- Keep each JSON or status reply on one line. Clients such as PanelDue discard a partial object when they see a newline.\n- If the panel has its own buzzer, add it to the `USE_MARLINUI_BUZZER` list in `Conditionals-5-post.h` and define `MarlinUI::buzz()` in the backend. Without a `BEEPER_PIN`, `HAS_SOUND` is off and `M300` isn't compiled.\n\n## Verify\n\n1. Build every backend the change touches. Find the tests that cover them:\n\n       grep -rliE \"elegoo_neptune_3_tft|dwin_lcd_proui|touch_ui_ftdi_eve|extui_example|dgus_lcd_ui\" buildroot/tests\n\n   Build one with `buildroot/bin/mftest -t <env> -n <N> -y`.\n\n2. Backends with no CI test need a manual build. Use an STM32 board, not AVR RAMPS, because serial LCDs on RAMPS fail on a pin conflict with Serial Port 3:\n\n       buildroot/bin/restore_configs\n       buildroot/bin/opt_set MOTHERBOARD BOARD_BTT_SKR_PRO_V1_1 SERIAL_PORT -1 LCD_SERIAL_PORT 1\n       buildroot/bin/opt_enable EEPROM_SETTINGS SDSUPPORT AUTO_BED_LEVELING_BILINEAR FIX_MOUNTED_PROBE Z_SAFE_HOMING PIDTEMPBED\n       buildroot/bin/opt_enable MALYAN_LCD            # or the backend's own option(s)\n       buildroot/bin/mftest -a -n1 -y\n\n   Some backends need extra options (for example `LCD_BED_TRAMMING` and `BABYSTEPPING` for Anycubic, `CLASSIC_JERK` for DGUS E3S1PRO). Read the first error.\n\n3. Judge each build by its log: count `[SUCCESS]` and `error:`. Don't pass `-s` to `mftest`, which hides the log. Run builds one at a time in a single clone, since parallel builds collide on the `.pio` state.\n4. Confirm an override really happened. With the ARM toolchain's `nm`:\n\n       arm-none-eabi-nm -C .pio/build/<env>/firmware.elf | grep -E ' ExtUI::on[A-Za-z]+\\('\n\n   `T` is the backend's own definition, and `W` is the weak default. A handler you defined that still shows `W` wasn't compiled, so check the guards around it.\n\n5. Run `buildroot/bin/restore_configs` between builds and at the end. `git status --short` should then show only your source edits.\n6. For a serial-panel backend, also run it end to end in the simulator with a scripted panel emulator. Check that replies are valid single lines, that the panel is still answered while Marlin is busy, and that each translated command takes effect.\n","buildroot/skills/marlin-hal-work/SKILL.md":"---\nname: marlin-hal-work\ndescription: Use when adding a board to an existing HAL, extending a HAL, creating a new HAL, or debugging HAL-specific code. Includes the board checklist, comparing HALs, and verifying framework APIs.\n---\n\n# Marlin HAL and Board Work\n\nEach HAL folder `Marlin/src/HAL/<FAMILY>/` has its own `AGENTS.md` with the build and test loop and the gotchas for that family. Read it first.\n\nThree levels of work, from most to least common:\n\n1. **Add a board** to an existing HAL.\n2. **Extend a HAL** with an MCU variant.\n3. **Create a new HAL** for a new architecture.\n\n## Compare, don't generalize\n\nDon't assume one HAL's behavior holds for another. Timers, ISR entry, serial buffering, and ADC differ a lot. To learn the contract, compare three representatives: a simple one (AVR), a mid one (LPC1768), and a complex one (STM32 or ESP32).\n\n    ls Marlin/src/HAL/*/                      # a file in 10+ HALs is probably required\n    head -80 Marlin/src/HAL/{AVR,LPC1768,STM32,RP2040}/timers.h\n    head -50 Marlin/src/HAL/{AVR,LPC1768,STM32,RP2040}/fastio.h\n\n`Marlin/src/HAL/shared/` holds what every HAL inherits (`Marduino.h`, `serial_ports.h`, `eeprom_api.h`, ...). `Marlin/src/HAL/platforms.h` is the single dispatch point: a wrong detection macro loads the wrong HAL, and the failure is confusing.\n\n## Adding a board\n\n| Step | File | Action |\n| --- | --- | --- |\n| Pins | `Marlin/src/pins/<arch>/pins_<BOARD>.h` | All `*_PIN` defines. Use a `_common.h` file when variants share a pinout |\n| Board ID | `Marlin/src/core/boards.h` | `#define BOARD_<NAME> <ID>` in the right architecture range, highest ID + 1 |\n| Routing | `Marlin/src/pins/pins.h` | `#elif MB(<NAME>)` in the same relative position as in `boards.h` |\n| Environment | `ini/<arch>.ini` | Add an `[env:...]` that `extends` a similar one |\n| Validation | `Marlin/src/pins/<arch>/env_validate.h` | Only if the family has a unique constraint |\n| CI test | `buildroot/tests/<env>/` | Optional |\n\nDo not edit `config.ini` for a board.\n\nConventions:\n\n- Letters between `pins_` and `.h` are UPPERCASE. The `_common` suffix is lowercase. Prefix the manufacturer's name if the board name isn't distinctive.\n- The `#define` order in `boards.h` must match the `#elif MB()` order in `pins.h`.\n- Set `BOARD_INFO_NAME` (and optionally `DEFAULT_MACHINE_NAME`) in the board's pins file.\n- Use `#ifndef` guards around pins that variants may override.\n- The EEPROM backend is chosen per board: most boards define `FLASH_EEPROM_EMULATION` or `SDCARD_EEPROM_EMULATION` in the pins file. If none is enabled you get an `undefined reference to PersistentStore::*` at link.\n- Link the schematic or source of the pin data in a comment, if there is one.\n\n### Verifying pin and UART assignments\n\nCheck open sources before saying a pin can't be verified without hardware. Cross-check at least two:\n\n1. Klipper configs (`Klipper3d/klipper/config/printer-<vendor>-<model>*.cfg`, `generic-<board>.cfg`). They list step/dir/enable/heater/fan pins, and the MCU serial pins in comments.\n2. Vendor or community Marlin forks: `pins_*.h`, the variant's `variant.h` and `PeripheralPins.c`, and the ini env's `build_flags`.\n3. Elimination: a UART has only a few alternate-function pin pairs. If the others are used by steppers or heaters, the one left is the answer.\n\nSTM32duino uses `PIN_SERIALn_RX/TX` if defined. Otherwise it takes the first matching entry in the variant's pin map. A board whose UART is on a later pair needs `-DPIN_SERIALn_RX=...` and `-DPIN_SERIALn_TX=...` in its env.\n\n### Validate with mftest\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_<NAME>\n    buildroot/bin/mftest -a -n1 -y\n\nRaw `pio run -e <env>` reads whatever `Configuration.h` was last generated, and can report a phantom `Build environment is incompatible with BOARD_...`. Trust `mftest` and don't chase a pio-only \"incompatible\" error. A passing build only shows the files are structurally sound. Say so in the PR if no one has tested the board.\n\n## Creating a new HAL\n\nThe minimum set that must exist to compile:\n\n- A branch in `platforms.h`\n- `HAL.h` and `HAL.cpp` (the `MarlinHAL` class)\n- `fastio.h`\n- `timers.h` and `timers.cpp`\n- `MarlinSerial.h` and `MarlinSerial.cpp`\n- `inc/Conditionals-2-LCD.h` and `inc/SanityCheck.h`, plus the other files in `inc/`, which can be empty (`#pragma once`) but must exist\n\nUse a recent HAL such as HC32 as the layout reference. Everything else fills in as features are enabled.\n\n## Verify against the real framework\n\nCode written without the vendor framework in view tends to invent names borrowed from another platform (for example `GPIO_TypeDef`, `BSRR`, `Pin_TypeDef`). The installed framework (`~/.platformio/packages/framework-*`) is ground truth: grep it before writing or \"fixing\" any peripheral symbol. A symbol that isn't there is a HAL bug, not a missing dependency.\n\n## Pitfalls\n\n- A HAL header missing `#include \"../../inc/MarlinConfig.h\"` makes shared files report `FSTR_P`, `PGM_P`, `millis_t`, or `pin_t` undefined. Fix the one include, not the cascade. Files in `HAL/<ARCH>/<sub>/` need `../../../inc/MarlinConfig.h`.\n- A timer ISR macro with no vector or NVIC wiring compiles but never fires.\n- `MF_TIMER_STEP`, `MF_TIMER_TEMP`, and `MF_TIMER_PWM` must not collide with each other or with platform-reserved timers.\n- `CRITICAL_SECTION_START/END` must nest (save and restore the previous interrupt state). Don't use bare `__disable_irq()` / `__enable_irq()`.\n- `pin_t` must match the platform's GPIO numbering.\n- `SERIAL_PORT` numbering differs between HALs (AVR 0-3, STM32 and RP2040 -1/0/1).\n- A serial adapter class must subclass the real core serial class. Forward-declaring it gives \"incomplete type\" errors.\n- Some cores lack the bare `Serial` global or `spiBegin()`, which Marlin code calls.\n- Vendored framework files may use CRLF. Some editors and tools turn an escape such as `\"\\r\\n\"` into a real control character, so use hex constants like `0x0D`.\n- Don't remove or break the AVR HAL. It is the reference implementation.\n- Some boards must keep the base AVR flash and RAM budget. Don't let the base build grow.\n","buildroot/skills/marlin-pr-workflow/SKILL.md":"---\nname: marlin-pr-workflow\ndescription: Use when preparing, splitting, or submitting a Marlin Pull Request. Covers PR size, branch, commit messages, rebase, and the PR template.\n---\n\n# Marlin Pull Request Workflow\n\nPolicy background: `.github/contributing.md` (AI-Assisted Contributions) and `AGENTS.md` section 11.\n\n## 1. Keep it small\n\nA PR does one thing and builds on its own. If your working tree holds more than one logical change, or a reviewer would struggle to follow it, split it into two or more PRs:\n\n- refactor first, behavior change second\n- core logic first, LCD/UI support second\n- one HAL, board family, or pin file group per PR\n- unrelated fixes in their own PRs\n\nTo split: create a new branch from `upstream/bugfix-2.1.x` for each piece and bring over only its changes (`git checkout <work-branch> -- <paths>` or `git cherry-pick -n`, then commit selectively with `git add -p`). Build each branch separately. State any dependency between the PRs in their descriptions.\n\n## 2. Branch\n\n    git fetch upstream\n    git checkout -b my_change upstream/bugfix-2.1.x\n\nPRs target `bugfix-2.1.x`, never a release branch.\n\n## 3. Before committing\n\n- Format only your changed files (skill: marlin-code-style).\n- Build the affected environments (skill: marlin-build-test).\n- Restore the config files after testing (`buildroot/bin/restore_configs` discards uncommitted edits to them, so commit any real config changes first). Don't commit test-only config edits.\n- Add tests for any new feature or option: a `buildroot/tests/<env>/config-NN.ini` build test, and a unit test under `Marlin/tests` when the logic can run without hardware (skill: marlin-build-test).\n- Review `git diff` for unrelated changes.\n\n## 4. Commit\n\n- Imperative mood, 72 characters or fewer on the first line, gitmoji-style emoji prefix like recent `git log` entries (🐛 fix, ✨ feature, ⚡️ performance, 🔧 config, 🩹 minor fix, 🧑‍💻 developer, 🚸 UX).\n- One logical change per commit. Squash fixups.\n\n## 5. Keep current\n\nRebase, don't merge:\n\n    git fetch upstream\n    git rebase upstream/bugfix-2.1.x\n    git push -f\n\n## 6. Open the PR\n\nFill in every section of `.github/pull_request_template.md`. No issue numbers in the title. List what you built and tested, and on what hardware if any. Attach a Configurations ZIP if reviewers need one to test.\n\nThe `mf*` helper scripts (`mfnew`, `mfrb`, `mfpr`, ...) described at https://marlinfw.org/docs/development/git_scripts.html are optional conveniences for these steps.\n","buildroot/skills/marlin-unit-tests/SKILL.md":"---\nname: marlin-unit-tests\ndescription: Use when writing or running Marlin's native unit tests (make unit-test-*), or when a native unit-test build fails locally, especially on macOS.\n---\n\n# Marlin Native Unit Tests\n\nBackground: `test/README.md`. For adding a test to a feature PR, see also the unit-test section of skill marlin-build-test.\n\n## Layout\n\n- Test sources: `Marlin/tests/<area>/test_<name>.cpp` (Unity, `MARLIN_TEST(group, name)`).\n- Test configurations: `test/NNN-name.ini`. Each one builds a separate test binary with a different Marlin configuration. Suites are discovered from this folder by `buildroot/share/PlatformIO/scripts/collect-code-tests.py`.\n- The PlatformIO environment is `linux_native_test` in `ini/native.ini`. It is built on the Linux native HAL, with `-Werror`.\n\n## Run\n\n    make unit-test-all-local                              # all configs\n    make unit-test-single-local UNIT_TEST_CONFIG=default  # one config (test/001-default.ini)\n    make unit-test-all-local-docker                       # same, in a container\n\n## Write one\n\n1. Copy the license header and macros from an existing test, such as `Marlin/tests/feature/test_runout.cpp`.\n2. Wrap the file in `#if ENABLED(FEATURE)` so it compiles only for configs that enable the feature.\n3. If no `test/*.ini` enables the feature, add one with `motherboard = BOARD_SIMULATED` and only the options required to pass the sanity checks.\n4. Test logic, not hardware: parsing, math, conversions, state machines. Anything needing real pins or timers isn't a good candidate.\n\n## macOS\n\nOn macOS `/usr/bin/gcc` is Clang, which cannot build Marlin. Install GCC with MacPorts or Homebrew, then:\n\n    buildroot/bin/mac_gcc macports      # or: homebrew   (apple resets to the system compiler)\n    which gcc                           # should now be the real GCC\n\nIf the native build fails on macOS, first check the compiler diagnostics and which `gcc` you got. Don't assume the failure is a bug in the test wiring.\n\n## Pitfalls\n\n- Test files are compiled with `-Werror`. An unused variable or sign-compare warning fails the run.\n- Don't work around a compiler warning with duplicate includes or duplicate keys in the INI.\n- Run the test configs one at a time. They share the `.pio` build directory.\n","AGENTS.md":"# Marlin 3D Printer Firmware\n\n> This file consolidates the project summary (`docs/project-summary.md`) into the top-level agent reference. It is the canonical orientation document for the whole repo. Per-HAL working notes live in `Marlin/src/HAL/<FAMILY>/AGENTS.md` (e.g. `Marlin/src/HAL/AT32/AGENTS.md`).\n\n## 1. Executive Summary\n\nMarlin is an open-source firmware for 3D printers, written in C/C++ and built on the Arduino framework via PlatformIO. It sits between a host computer (running slicer software) and the printer's hardware, translating G-code movement commands into precise stepper motor control, temperature regulation, and peripheral management. The firmware supports 15+ microcontroller platforms (STM32, AVR, ESP32, RP2040, and more), 400+ pin configuration files across 24 board families, and a modular feature system with 80+ enable/disable features. At over 2,500 source files, Marlin is one of the most widely deployed 3D printer firmware projects in the world, licensed under GPL v3.0.\n\n## 2. Architecture Overview\n\nMarlin follows a layered embedded firmware architecture organized around a Hardware Abstraction Layer (HAL), a G-code command processing pipeline, a set of core motion/thermal modules, and a rich feature system. The firmware's singleton entry point is the `Marlin` class (`MarlinCore`), which manages global state and the main control loop.\n\n```mermaid\ngraph TB\n    subgraph Environment[\"Printer Environment\"]\n        Host[\"Host Computer\\n(Slicer / Host Software)\"]\n        SD[\"SD Card\\n(SdFat)\"]\n        Sensors[\"Thermistors &\\nEndstops\"]\n        Motors[\"Stepper Motors\\n&amp; Drivers\"]\n        Heaters[\"Heaters &amp; Fans\"]\n        Display[\"LCD / TFT / DWIN\"]\n    end\n\n    subgraph Marlin[\"Marlin Firmware\"]\n        subgraph GCode[\"G-Code Subsystem\"]\n            Queue[\"GCodeQueue\\nRing Buffer\"]\n            Parser[\"GCodeParser\\nParameter Extraction\"]\n            Suite[\"GcodeSuite\\nCommand Dispatcher\"]\n        end\n        subgraph Modules[\"Core Modules\"]\n            Motion[\"Motion Planner\"]\n            Planner[\"Block Planner\"]\n            Stepper[\"Stepper Driver\"]\n            Temp[\"Temperature Manager\"]\n        end\n        subgraph Features[\"Feature System\\n80+ modules\"]\n            BedLevel[\"Bed Leveling\"]\n            PowerLoss[\"Power Loss Recovery\"]\n            Runout[\"Filament Runout\"]\n            MMU[\"Multi-Material\\nUnit\"]\n        end\n        subgraph HAL[\"Hardware Abstraction Layer\"]\n            Shared[\"HAL Shared Code\"]\n            Platforms[\"15+ Platform HALs\"]\n        end\n    end\n\n    Host -->|\"USB Serial\\n250k baud\"| Suite\n    SD -->|\"SD Interface\"| Queue\n    Suite --> Parser\n    Parser --> Suite\n    Queue --> Suite\n    Suite --> Motion\n    Suite --> Temp\n    Motion --> Planner\n    Planner --> Stepper\n    Stepper --> Motors\n    Temp --> Heaters\n    Temp --> Sensors\n    Display <-->|\"SPI/I2C/UART\"| Suite\n```\n\nThe firmware communicates with the host via serial at 250,000 baud (configurable), receiving G-code commands line-by-line. Commands are queued in a circular buffer (`GCodeQueue::ring_buffer`), parsed by `GCodeParser`, and dispatched by `GcodeSuite` to category-specific handlers (motion, temperature, configuration, calibration, etc.). The core modules handle the low-level real-time tasks: the planner generates motion blocks with jerk control and acceleration profiling, the stepper driver issues pulse trains, and the temperature manager runs PID loops on thermistor inputs.\n\n### Supported Hardware Platforms\n\n| Platform Family | MCUs | Architecture | Notes |\n| --- | --- | --- | --- |\n| STM32 (various) | STM32F0, F1, F4, F7, H7, G0 | ARM Cortex-M | Primary 32-bit target |\n| AVR | ATmega2560, ATmega1280, AT90USB | 8-bit AVR | Legacy support |\n| ESP32 | ESP32-S3, ESP32-S2 | Xtensa/RISC-V | WiFi/BLE capability |\n| SAMD | SAMD21, SAMD51 | ARM Cortex-M0+/M4 | Adafruit Feather M4 |\n| RP2040 | RP2040, RP2350 | ARM Cortex-M33 | Raspberry Pi Pico |\n| LPC | LPC1768, LPC1769 | ARM Cortex-M3 | RAMBo, Melzi boards |\n| GD32 | GD32F1, GD32F3 | ARM Cortex-M3 | GigaDevice clones |\n| HC32 | HC32F4 | ARM Cortex-M4 | JCEC chips |\n| AT32 | AT32F4 | ARM Cortex-M4 | Artery Tek |\n| Teensy | 3.1/3.2, 3.5/3.6, 4.0/4.1 | ARM Cortex-M4/M7 | PJRC boards |\n| Linux Simulator | x86/x64 | Native | CI/testing |\n| Native Simulator | Any | Native | Unit testing |\n\n## 3. Processing Pipeline\n\nThe G-code processing pipeline follows a well-defined flow from input ingestion through parsing, dispatch, and hardware actuation.\n\n```mermaid\nflowchart TD\n    InputSerial[\"Serial Input\\nUSB / UART\"] --> Queue\n    InputSD[\"SD Card\\nSdFat\"] --> Queue\n    InputProgmem[\"Program Memory\\nPROGMEM\"] --> Queue\n\n    Queue[\"GCodeQueue\\nRing Buffer\\nBUFSIZE entries\"] --> Parser\n\n    Parser[\"GCodeParser\\n• Letter/Code/Subcode\\n• Parameter extraction\\n• FASTER_GCODE_PARSER\\n  pre-scanned flags\"] --> Suite\n\n    Suite[\"GcodeSuite\\nCommand Dispatcher\\n• Subcommand routing\\n• Parameter validation\\n• Endstop events\"]\n\n    Suite --> CatMotion[\"Motion\\nG0/G1/G2/G3\"]\n    Suite --> CatTemp[\"Temperature\\nM104/M105/M106/M107/M109\"]\n    Suite --> CatConfig[\"Configuration\\nM200-M205/M301/M92\"]\n    Suite --> CatCalibrate[\"Calibration\\nG28/G33/G34/G425\"]\n    Suite --> CatSD[\"SD Card\\nM20-M34/M928\"]\n\n    CatMotion --> Planner\n    CatTemp --> Temp\n    CatCalibrate --> Motion\n\n    Planner[\"Block Planner\\n• Trapezoidal profile\\n• Jerk control\\n• Buffer: BLOCK_BUFFER_SIZE\"] --> Stepper\n    Motion[\"Motion\\n• axis_position()\\n• relative_mode\\n• dual_x_carriage\"] --> Planner\n    Temp[\"Temperature\\n• PID loops\\n• thermistor tables\\n• safety checks\"] --> Heaters\n\n    Stepper[\"Stepper Driver\\n• issue_pending()\\n• TMC SPI config\\n• Trinamic drivers\"] --> Output[\"Hardware Outputs\\n• Step/Dir pulses\\n• PWM heat/fan\\n• Serial responses\"]\n```\n\n### Key Pipeline Components\n\n**GCodeQueue** — A circular ring buffer (`RingBuffer`) that holds up to `BUFSIZE` G-code command strings. Commands enter via three injectors: serial input (USB/UART), SD card file reading, and in-firmware injected commands (PROGMEM). The queue's `advance()` method pops the next command and hands it to the parser.\n\n**GCodeParser** — Parses a single G-code line, extracting the command letter (G/M/T), code number, subcode, and all parameter values (X, Y, Z, E, F, S, P, etc.). When `FASTER_GCODE_PARSER` is enabled, the parser pre-scans all parameters into a flags array for O(1) lookups.\n\n**GcodeSuite** — The command dispatcher singleton. Each G/M code maps to a handler method within this class. Commands are routed to category subdirectories under `src/gcode/` (e.g., `motion/`, `temp/`, `config/`, `calibrate/`). The suite also handles pre- and post-command hooks for endstop events, buffer monitoring, and inactivity shutdown.\n\n## 4. Core Components\n\n### 4.1 Firmware Entry Point\n\nThe firmware lifecycle is managed by `MarlinCore.cpp` (~1,777 lines), structured around Arduino's `setup()` and `loop()` functions:\n\n- **`setup()`** — Initializes HAL pins, serial ports, all modules (stepper, temperature, motion, planner, settings), features (runout detection, power loss recovery, LEDs), and the UI subsystem. Each initialization block is wrapped in `SETUP_RUN()` for debug logging in dev mode.\n- **`loop()`** — Runs continuously (infinite loop on AVR, structured loop on 32-bit platforms). Each iteration calls `marlin.idle()`, processes the G-code queue, checks power-off timers, and handles endstop events.\n\n### 4.2 Singleton Architecture\n\nGlobal state is managed through singletons:\n\n| Singleton | File | Responsibility |\n| --- | --- | --- |\n| `marlin` | `MarlinCore.cpp/h` | Global state machine, inactivity management, kill/suicide, heatup waits |\n| `gcode` | `gcode/gcode.cpp` | G-code command dispatch |\n| `queue` | `gcode/queue.h` | Command queue ring buffer |\n| `planner` | `module/planner.cpp` | Motion block buffer and trapezoidal profiling |\n| `stepper` | `module/stepper.cpp` | Step pulse generation and TMC driver config |\n| `thermalManager` | `module/temperature.cpp` | PID loops, thermistor reading, safety monitoring |\n| `card` | `sd/cardreader.cpp` | SD card file system operations |\n| `ui` | `lcd/marlinui.cpp/h` | LCD/UI state machine and menu system |\n\n### 4.3 Module System\n\nThe `module/` directory contains the core motion and thermal management classes:\n\n| Module | File | Description |\n| --- | --- | --- |\n| **Motion** | `motion.h/cpp` | High-level axis positioning, coordinate transforms, joint interpolation |\n| **Planner** | `planner.h/cpp` | Block buffer, trapezoidal velocity profiling, junction deviation |\n| **Stepper** | `stepper.h/cpp` | Step pulse generation, direction control, TMC Trinamic SPI configuration |\n| **Temperature** | `temperature.h/cpp` | PID autotune (M303), thermistor tables, hotend/bed/heater management |\n| **Endstops** | `endstops.h/cpp` | Endstop interrupt handling, homing logic, soft endstops |\n| **Probe** | `probe.h/cpp` | Bed probing (G30, G31/G32, G38, bltouch), Z-offset management |\n| **Settings** | `settings.h/cpp` | EEPROM persistence, configuration storage and retrieval |\n| **PrintCounter** | `printcounter.h/cpp` | Print time, filament usage, and job statistics |\n| **Servo** | `servo.h/cpp` | Servo control for solenoids, lid mechanisms |\n| **Delta/Scara/Polar** | `delta.h`, `scara.h`, `polar.h` | Kinematic transforms for non-Cartesian printers |\n| **Tool Change** | `tool_change.h/cpp` | Multi-extruder tool switching, hotend offset, PTFE purge |\n\n### 4.4 Feature System\n\nThe `feature/` directory contains 80+ optional modules, each gated by a `#if ENABLED(FEATURE_NAME)` preprocessor check:\n\n**Motion Features:** Linear Advance (`linearadvance`), Pressure Advance, Adaptive Multi-Axis Stepping, Direct Stepping, Bresenham acceleration, Resonance Compensation, X-Axis Twist Correction, Z-Stepper Alignment, Backlash Compensation.\n\n**Thermal Features:** Automatic PID Tuning (M303), Thermal Protection, Probe Temperature Compensation, Mixed Extruder (`mixing`), Bowden/Direct Drive Retraction (`fwretract`).\n\n**Peripheral Features:** Power Loss Recovery (`powerloss`), Filament Runout Detection (`runout`), Ethernet Connectivity (`ethernet`), RS485 Communication (`rs485`), MMU/Multi- Material Unit (`mmu`, `mmu3`), Solenoid Control (`solenoid`), Spindle/Laser (`spindle_laser`), Case Lights (`caselight`), LED Color Control (`leds`).\n\n**UI Features:** Touch Screen (`touch`), LVGL TFT UI (`mks_ui`), DWIN Display (`dwin`), Extensible UI (`extui`), Password Protection (`password`), Joystick Input (`joystick`).\n\n**Bed Leveling:** Unified Bed Leveling (UBL), Manual Bed Leveling (MBL), Automatic Bed Leveling (ABL), G26 Mesh Validation, G35 Auto Bed Leveling, Bed Level Visualizer.\n\n## 5. G-Code Command Reference\n\nMarlin implements 100+ G-code commands organized into 14 categories under `src/gcode/`:\n\n| Category | Directory | Key Commands | Description |\n| --- | --- | --- | --- |\n| **Motion** | `motion/` | G0, G1, G2, G3, G5, G6, G80, M400, M290 | Linear/arc moves, rapid moves, dwell, wait, jog |\n| **Temperature** | `temp/` | M104/M105/M106/M107/M109, M140/M190, M303 | Hotend/bed temp set/read, fan control, PID autotune |\n| **Configuration** | `config/` | M200-M205, M301, M92, M43, M218, M217 | Extruder steps, accel/jerk, PID, hotend offset, bowden length |\n| **Calibration** | `calibrate/` | G28, G33, G34, G425, M48, M566, M665 | Home, delta calibration, nozzle purge, backlash, bellows |\n| **Geometry** | `geometry/` | G17-G19, G53-G59, G92, M206, M428 | Plane selection, coordinate systems, position reset, offsets |\n| **Control** | `control/` | M17/M18/M84, M80/M81, M3-M5, M7-M9, T | Enable axes, power, spindle, tool select, feedrate/extruder units |\n| **Probe** | `probe/` | G30, G31/G32, G38, M851, M401/M402, M102 | Bed probe, probe type, probe actions |\n| **SD Card** | `sd/` | M20-M34, M928, M1001, M1003 | File listing, select, start, stop, abort, load, save |\n| **LCD** | `lcd/` | M0, M1, M73, M117, M145, M250, M300 | Pause, progress, message, color, beep |\n| **Host** | `host/` | M16, M110, M113, M114, M115, M118, M119, M154, M360, M876 | Line numbers, machine info, endstop report, kinematic config |\n| **EEPROM** | `eeprom/` | M500, M501, M502, M503, M504 | Save/load/reset/settings EEPROM operations |\n| **Stats** | `stats/` | M31, M75-M78 | Print time, filament used, errors, buffer stats |\n| **Units** | `units/` | G20/G21, M82/M83, M149 | mm/inch, extrusion mode, temp unit display |\n| **OTA** | `ota/` | M936 | Over-the-air firmware update |\n\n## 6. Infrastructure & Deployment\n\n### 6.1 Build System\n\nMarlin uses PlatformIO as its primary build system, configured via `platformio.ini`:\n\n- **Framework**: Arduino (via PlatformIO)\n- **Build flags**: `-g3 -D__MARLIN_FIRMWARE__ -DNDEBUG -fsingle-precision-constant`\n- **Source filter**: Dynamic inclusion/exclusion of files based on target board via pre-build scripts (`configuration.py`, `common-dependencies.py`, `preflight-checks.py`)\n- **Include directory**: `Marlin/src/`\n- **Board definitions**: Custom PlatformIO board definitions in `buildroot/share/PlatformIO/boards/`\n\n### 6.2 Configuration System\n\nConfiguration is managed through a layered preprocessor conditional system:\n\n1. **`Configuration.h`** — User-editable hardware and feature configuration\n2. **`Configuration_adv.h`** — Advanced/experimental feature toggles\n3. **`Version.h`** — Build version, machine name, website URL\n4. **`inc/Conditionals-*.h`** — Auto-generated feature flags derived from Configuration.h\n5. **`inc/MarlinConfig.h`** — Prefix header including all conditionals, types, and sanity checks\n\nThe conditional system ensures that only enabled features compile, minimizing firmware footprint for resource-constrained 8-bit platforms.\n\n### 6.3 CI/CD Pipeline\n\nGitHub Actions workflows run on every pull request and push:\n\n| Workflow | Purpose |\n| --- | --- |\n| `ci-build-tests.yml` | Compiles Marlin for all supported boards (matrix strategy) |\n| `ci-unit-tests.yml` | Runs C++ unit tests on the native simulator |\n| `ci-validate-boards.yml` | Validates board pin configurations |\n| `ci-validate-pins.yml` | Checks for missing/invalid pin definitions |\n| `ci-validate-lines.yml` | Validates line count limits for 8-bit boards |\n| `check-pr.yml` | PR quality checks |\n\n### 6.4 Docker Build Environment\n\nA Dockerfile (`docker/Dockerfile`) provides a reproducible build environment:\n\n```dockerfile\nFROM python:3.11-bookworm\nRUN pip install -U platformio PyYaml\nWORKDIR /code\n```\n\n### 6.5 Testing\n\n- **Unit tests**: Located in `Marlin/tests/`, run via the Linux native simulator HAL\n- **Build tests**: CI compiles against 20+ board targets to catch compile errors\n- **Pin validation**: Automated checks for pin conflicts and missing definitions\n- **SdFat**: Integrated SdFat library for SD card file operations (Sd2Card, SdBaseFile, SdVolume)\n\n## 7. Extension Patterns\n\n### 7.1 Adding a New Feature\n\n1. Create your feature files in `Marlin/src/feature/` (e.g., `my_feature.h` and `my_feature.cpp`)\n2. Add `#define MY_FEATURE` to `Configuration_adv.h`\n3. The conditional system in `inc/Conditionals-*.h` will auto-generate `HAS_MY_FEATURE`\n4. Use `#if ENABLED(MY_FEATURE)` guards in your code\n5. Include your header from `MarlinCore.cpp` under the appropriate `#if ENABLED()` block\n6. Register G-code handlers in `gcode/gcode.cpp` or a new category subdirectory\n\n### 7.2 Adding a New G-Code Command\n\n1. Create a new file in the appropriate `src/gcode/<category>/` directory (e.g., `M1000.cpp`)\n2. Implement the handler method in the `GcodeSuite` class\n3. Register the command in the `process_commands()` method of `GcodeSuite`\n4. The command will be dispatched automatically when the parser encounters the letter/code\n\n### 7.3 Adding Board Support\n\n1. Create a new PlatformIO board definition in `buildroot/share/PlatformIO/boards/`\n2. Add pin definitions in `Marlin/src/pins/<family>/pins_<board>.h`\n3. If the MCU is new, add HAL support in `Marlin/src/HAL/<MCU_FAMILY>/`\n4. Update `ini/<platform>.ini` in `platformio.ini`'s `extra_configs`\n5. Run `ci-validate-pins.yml` to verify pin assignments\n\n### 7.4 Adding a Display Backend\n\n1. Create backend files in `Marlin/src/lcd/<backend>/`\n2. Implement the required display interface (SPI/I2C/UART)\n3. Add `#define ULTIPANEL` or display-specific `#define` to `Configuration.h`\n4. The LCD subsystem routes through `MarlinUI` to the appropriate backend\n\n## 8. Rules & Anti-Patterns\n\n### Best Practices\n\n- **Conditional compilation**: Always gate platform-specific code with `#if ENABLED()` or `#ifdef ARDUINO_ARCH_XXX`\n- **PROGMEM usage**: Store large strings and lookup tables in program memory on 8-bit platforms\n- **Interrupt safety**: Use `WAIT`/`NOOP` patterns for stepper ISR synchronization\n- **EEPROM limits**: Respect memory constraints — 8-bit boards have limited EEPROM (4KB-8KB)\n- **Thread safety**: The main loop and stepper ISR share state — use volatile and careful locking\n- **Static analysis**: Use `bug_on()` macros for compile-time assertion checks\n- **Code size**: 8-bit targets (AVR) have strict flash limits — enable only necessary features\n\n### Anti-Patterns\n\n- **Don't add features to `Configuration.h`** — use `Configuration_adv.h` for advanced/experimental features\n- **Don't modify HAL shared code** without understanding platform implications\n- **Don't use floating-point on AVR** without explicit `float` typing — the compiler defaults to single precision\n- **Don't block in the main loop** — all long operations must yield to `idle()` or use non-blocking patterns\n- **Don't assume 32-bit semantics** — code must compile for both 8-bit AVR and 32-bit ARM\n- **Don't hardcode pin numbers** — always use the pin definition headers\n\n## 9. Dependencies\n\n### Build Tools\n\n| Dependency       | Version           | Purpose                    |\n| ---------------- | ----------------- | -------------------------- |\n| PlatformIO       | Latest dev        | Build system and framework |\n| Arduino Core     | Platform-specific | Microcontroller SDK        |\n| GCC ARM Embedded | 12.x              | ARM cross-compiler         |\n| avr-gcc          | 7.x               | AVR cross-compiler         |\n\n### Libraries (integrated)\n\n| Library    | Purpose                                             |\n| ---------- | --------------------------------------------------- |\n| SdFat      | SD card file system (Sd2Card, SdBaseFile, SdVolume) |\n| u8glib     | OLED/LCD display driver (embedded in HAL)           |\n| LVGL       | TFT graphical UI (optional, for MKS UI)             |\n| heatshrink | G-code decompression for SD prints                  |\n\n### Platform Libraries\n\n| Platform | Libraries                     |\n| -------- | ----------------------------- |\n| STM32    | STM32 HAL/LL, STM32duino core |\n| AVR      | Arduino AVR core, TimerOne    |\n| ESP32    | ESP-IDF components, WiFi      |\n| RP2040   | Arduino RP2040 core, PIOasm   |\n| Teensy   | Teensyduino core, Bounce2     |\n| SAMD     | Arduino SAMD core             |\n\n## 10. Code Structure\n\n```\nMarlinFirmware/                  # Repo root (this directory). `cd` here for PlatformIO builds.\n├── Marlin/                      # The Arduino \"sketch\" (application source + config files)\n│   ├── Configuration.h          # User hardware configuration\n│   ├── Configuration_adv.h      # Advanced/experimental toggles\n│   ├── Marlin.ino               # Board-specific entry stub\n│   ├── Makefile                 # Alternative build (simulator)\n│   ├── Version.h                # Build version, machine name\n│   ├── config.ini               # PlatformIO extra config\n│   ├── lib/                     # Integrated libraries\n│   └── src/                     # Application source (see below)\n├── buildroot/                   # Build infrastructure\n├── ini/                         # PlatformIO platform configs\n├── docs/                        # Project documentation\n├── docker/                      # Docker build environment\n├── .github/workflows/           # CI/CD pipelines\n└── platformio.ini               # Main PlatformIO configuration\n```\n\n### Marlin/src layout\n\n```\nMarlin/src/\n├── MarlinCore.cpp/h         # Firmware entry, setup(), loop(), Marlin singleton\n├── core/                    # Core utilities (serial, language, types, mstring)\n│   ├── gcode/                   # G-code subsystem\n│   │   ├── gcode.cpp/h          # GcodeSuite dispatcher\n│   │   ├── parser.cpp/h         # GCodeParser\n│   │   ├── queue.cpp/h          # GCodeQueue ring buffer\n│   │   ├── motion/              # G0-G6, M290, M400\n│   │   ├── temp/                # M104-M109, M140-M193, M303\n│   │   ├── config/              # M200-M205, M301, M92, M218\n│   │   ├── calibrate/           # G28, G33, G34, G425, M48\n│   │   ├── bedlevel/            # G26, G35, G42, M420\n│   │   ├── geometry/            # G17-G19, G53-G59, G92\n│   │   ├── probe/               # G30-G38, M851, M401\n│   │   ├── sd/                  # M20-M34, M928\n│   │   ├── lcd/                 # M0, M1, M73, M117\n│   │   ├── host/                # M16, M110-M119\n│   │   ├── eeprom/              # M500-M504\n│   │   ├── stats/               # M31, M75-M78\n│   │   ├── units/               # G20/G21, M82/M83\n│   │   └── control/             # M3-M5, M7-M9, M17-M85, T\n│   ├── module/                  # Core modules\n│   │   ├── motion.h/cpp         # Axis positioning, kinematics\n│   │   ├── planner.h/cpp        # Block buffer, trapezoidal profiling\n│   │   ├── stepper.h/cpp        # Step pulses, TMC drivers\n│   │   ├── temperature.h/cpp    # PID, thermistors, safety\n│   │   ├── endstops.h/cpp       # Endstop interrupts, homing\n│   │   ├── probe.h/cpp          # Bed probing, Z-offset\n│   │   ├── settings.h/cpp       # EEPROM persistence\n│   │   ├── delta.h/cpp          # Delta kinematics\n│   │   ├── scara.h/cpp          # Scara kinematics\n│   │   ├── polar.h/cpp          # Polar kinematics\n│   │   ├── polargraph.h/cpp     # Polar graph kinematics\n│   │   ├── servo.h/cpp          # Servo control\n│   │   ├── printcounter.h/cpp   # Print statistics\n│   │   ├── tool_change.h/cpp    # Multi-extruder switching\n│   │   ├── ft_motion/           # Fine-tune motion\n│   │   └── stepper/             # Stepper internals, Trinamic\n│   ├── feature/                 # 80+ optional features\n│   │   ├── bedlevel/            # UBL, MBL, ABL\n│   │   ├── leds/                # LED color control\n│   │   ├── mmu/                 # Multi-material unit\n│   │   ├── mmu3/                # MMU3 variant\n│   │   ├── powerloss.h/cpp      # Power loss recovery\n│   │   ├── runout.h/cpp         # Filament runout detection\n│   │   ├── pause.h/cpp          # Pause/resume\n│   │   ├── mixing.h/cpp         # Mixed extruder\n│   │   ├── fwretract.h/cpp      # Firmware retraction\n│   │   ├── resonance/           # Resonance compensation\n│   │   ├── spindle_laser.h/cpp  # Spindle/laser control\n│   │   ├── ethernet.h/cpp       # Ethernet connectivity\n│   │   ├── solenoid.h/cpp       # Solenoid control\n│   │   ├── password/            # Password protection\n│   │   ├── joystick.h/cpp       # Joystick input\n│   │   ├── tmc_util.h/cpp       # TMC driver utilities\n│   │   └── ...                  # 60+ more features\n│   ├── lcd/                     # Display/UI subsystem\n│   │   ├── marlinui.h/cpp        # Main UI controller\n│   │   ├── HD44780/             # Hitachi HD44780 LCD\n│   │   ├── dogm/                # DOGM OLED/LCD\n│   │   ├── tft/                 # TFT displays\n│   │   ├── tft_io/              # TFT I/O drivers\n│   │   ├── dwin/                # DWIN displays\n│   │   ├── extui/               # Extensible UI (MKS LVGL)\n│   │   ├── menu/                # Menu system\n│   │   ├── touch/               # Touch screen\n│   │   ├── language/            # Multi-language strings\n│   │   └── sovol_rts/           # Sovol RTS display\n│   ├── libs/                    # Utility libraries\n│   │   ├── bresenham.h          # Bresenham line algorithm\n│   │   ├── vector_3.h/cpp       # 3D vector math\n│   │   ├── circularqueue.h      # Generic circular queue\n│   │   ├── adc/                 # ADC utilities\n│   │   ├── heatshrink/          # Decompression\n│   │   ├── nozzle.cpp/h         # Nozzle cleaning\n│   │   ├── numtostr.h/cpp       # Number-to-string\n│   │   ├── hex_print.h/cpp      # Hex printing\n│   │   ├── crc16.h/cpp          # CRC-16 calculation\n│   │   ├── stopwatch.h/cpp      # Stopwatch utility\n│   │   ├── least_squares_fit.h  # Least squares fitting\n│   │   └── ...                  # 15+ utility libs\n│   ├── sd/                      # SD card subsystem\n│   │   ├── cardreader.h/cpp     # Card reader interface\n│   │   ├── SdFat*               # SdFat library (file system)\n│   │   ├── disk_io_driver.h     # Disk I/O abstraction\n│   │   └── usb_flashdrive/      # USB flash drive support\n│   ├── pins/                    # Pin definitions (400+ files)\n│   │   ├── pins.h               # Pin inclusion router\n│   │   ├── ramps/               # Ramps board family\n│   │   ├── mega/                # Mega board family\n│   │   ├── rambo/               # Rambo board family\n│   │   ├── sanguino/            # Sanguino board family\n│   │   ├── stm32f1/             # STM32F1 boards\n│   │   ├── stm32f4/             # STM32F4 boards\n│   │   ├── stm32f7/             # STM32F7 boards\n│   │   ├── stm32h7/             # STM32H7 boards\n│   │   ├── stm32g0/             # STM32G0 boards\n│   │   ├── stm32f0/             # STM32F0 boards\n│   │   ├── sam/                 # SAM boards\n│   │   ├── samd/                # SAMD boards\n│   │   ├── rp2040/              # RP2040 boards\n│   │   ├── lpc1768/             # LPC1768 boards\n│   │   ├── lpc1769/             # LPC1769 boards\n│   │   ├── teensy2/             # Teensy 2.x boards\n│   │   ├── teensy3/             # Teensy 3.x boards\n│   │   ├── teensy4/             # Teensy 4.x boards\n│   │   ├── esp32/               # ESP32 boards\n│   │   ├── gd32f1/              # GD32F1 boards\n│   │   ├── gd32f3/              # GD32F3 boards\n│   │   ├── hc32f4/              # HC32F4 boards\n│   │   ├── at32f4/              # AT32F4 boards\n│   │   └── native/              # Native/simulator\n│   ├── HAL/                     # Hardware Abstraction Layer\n│   │   ├── HAL.h                # HAL interface definition\n│   │   ├── platforms.h          # Platform selection\n│   │   ├── shared/              # Cross-platform HAL code\n│   │   │   ├── HAL.cpp/h        # Shared HAL implementation\n│   │   │   ├── Delay.h          # Safe delay function\n│   │   │   ├── eeprom_api.h     # EEPROM API\n│   │   │   ├── cpu_exception/   # Exception handling\n│   │   │   └── backtrace/       # Stack backtrace\n│   │   ├── STM32/               # STM32 platform HAL\n│   │   ├── AVR/                 # AVR platform HAL\n│   │   ├── ESP32/               # ESP32 platform HAL\n│   │   ├── DUE/                 # Arduino Due HAL\n│   │   ├── SAMD21/              # SAMD21 HAL\n│   │   ├── SAMD51/              # SAMD51 HAL\n│   │   ├── RP2040/              # RP2040 HAL\n│   │   ├── LPC1768/             # LPC1768 HAL\n│   │   ├── GD32_MFL/            # GD32 HAL\n│   │   ├── HC32/                # HC32 HAL\n│   │   ├── AT32/                # AT32 HAL\n│   │   ├── TEENSY31_32/         # Teensy 3.1/3.2 HAL\n│   │   ├── TEENSY35_36/         # Teensy 3.5/3.6 HAL\n│   │   ├── TEENSY40_41/         # Teensy 4.0/4.1 HAL\n│   │   ├── STM32F1/             # STM32F1 HAL\n│   │   ├── LINUX/               # Linux simulator HAL\n│   │   └── NATIVE_SIM/          # Native simulator HAL\n│   └── tests/                   # Unit tests\n│       ├── unit_tests.h/cpp     # Test suite\n│       └── *.ini                # Test configurations\n├── docs/                        # Project documentation\n│   ├── AGENTS.md                # This document\n│   └── diagrams/                # Architecture diagrams\n│       ├── high-level-architecture.drawio\n│       ├── processing-pipeline.drawio\n│       └── component-relationships.drawio\n├── buildroot/                   # Build infrastructure\n│   ├── share/PlatformIO/        # PlatformIO board definitions & scripts\n│   ├── test-gcode/              # G-code test files\n│   └── tests/                   # Build test configurations\n├── ini/                         # PlatformIO platform configs\n│   ├── avr.ini, due.ini, esp32.ini\n│   ├── stm32-common.ini, stm32f1.ini, stm32f4.ini\n│   ├── stm32f7.ini, stm32h7.ini, stm32g0.ini\n│   ├── samd21.ini, samd51.ini\n│   ├── raspberrypi.ini, teensy.ini\n│   ├── at32.ini, gd32.ini, hc32.ini\n│   ├── lpc176x.ini, native.ini\n│   ├── features.ini, renamed.ini\n│   └── stm32f1-maple.ini\n├── test/                        # Root-level test configurations\n├── docker/                      # Docker build environment\n├── .github/workflows/           # CI/CD pipelines\n└── platformio.ini               # Main PlatformIO configuration\n```\n\n## 11. Constructing a Pull Request\n\nMarlin accepts AI-assisted code (see `.github/contributing.md`). It must be sensible and reviewable. Follow these rules when preparing a PR.\n\n### Scope and size\n\n- **One PR, one purpose.** A PR should be a single self-contained change that builds and passes CI on its own.\n- **Split large changes.** If a change touches several unrelated areas, or is large enough that a reviewer can't hold it in their head, split it into two (or more) smaller PRs. Typical splits: refactor first, feature second; core logic first, UI/LCD support second; one HAL or board family per PR; sanity checks and docs with the feature they cover.\n- **Order dependent PRs.** If PR B needs PR A, say so in B's description and don't open B until A is far enough along to be reviewed.\n- **No drive-by changes.** Don't reformat, rename, or tidy code you aren't changing. Keep the diff minimal.\n- **Don't change defaults** in `Configuration.h` / `Configuration_adv.h` unless that is the purpose of the PR. New features are disabled by default.\n\n### Before you start\n\n- Branch from the latest `bugfix-2.1.x` (the working branch). Never target release branches.\n- We occasionally backport fixes to lts-* branches. Very small non-duplicate patches are accepted.\n- Read the neighboring code and follow its conventions. Check `buildroot/skills/` for step-by-step procedures.\n- Read the HAL-specific `AGENTS.md` (`Marlin/src/HAL/<FAMILY>/AGENTS.md`) when touching a HAL.\n\n### Making the change\n\n- Code must compile on 8-bit AVR and 32-bit targets. Mind flash and RAM on AVR. Keep the base build requirements from growing.\n- Gate optional code with `ENABLED()` / `DISABLED()` / `ANY()` / `NONE()` / etc. and add validation to `Marlin/src/inc/SanityCheck.h`.\n- Use a suitable insertion point for Configuration and Conditionals. They follow a strict order set by `Marlin/src/inc/MarlinConfig.h`.\n- Don't hardcode pins; use the pin headers in `src/pins`.\n- When adding or changing a user-facing option, update its comments in `Configuration*.h` and note any `Conditionals`, `Warnings.cpp`, or `config.ini` impacts.\n- Follow the Marlin coding standards (2-space indent, no tabs, spacing as in nearby code). Indent new code similar to surroundings (including preprocessor nesting) and avoid auto-formatters.\n\n### Verifying\n\n- Use `MarlinSimUI` for direct testing in a simulated environment with a rich MCP interface. (And contribute your enhancements to `MarlinSimUI`.)\n- Build at least one affected environment (`buildroot/bin/mftest`; see the `marlin-build-test` skill).\n- **Add tests with new features and options.** Add or update a `buildroot/tests/<env>/config-NN.ini` build test so CI exercises the feature, along with its dependent options.\n- Add a unit test in `Marlin/tests/<area>/` (run with `make unit-test-all-local`) when the logic can be checked without hardware.\n  - Bug fixes should get a test when one can catch the regression. See the `marlin-build-test` skill.\n- Report honestly what was and wasn't tested. Never claim a build or test passed unless you ran it.\n\n### Commit messages and PR description\n\n- Imperative mood, first line of 72 characters or fewer, prefixed with a gitmoji-style emoji as in `git log` (e.g. 🐛 fix, ✨ feature, ⚡️ performance, 🔧 config, 🩹 minor fix, 🧑‍💻 developer tooling, 🚸 UX).\n- Don't put issue numbers in the PR title. Reference them in the description.\n- Fill in every section of `.github/pull_request_template.md`: Description, Requirements, Benefits, Configurations, Related Issues.\n- Keep history clean: rebase on `bugfix-2.1.x` rather than merging, and squash fixups into the commits they belong to.\n\n### Skills\n\nReusable procedures for contributors and their tools live in `buildroot/skills/`. Each skill is a folder with a `SKILL.md`. See `buildroot/skills/README.md`.\n","buildroot/skills/README.md":"# Marlin Contributor Skills\n\nStep-by-step procedures for working on Marlin, written for AI coding agents and equally useful as human cheat sheets. Each skill is a folder containing a `SKILL.md` with YAML front matter (`name`, `description`) followed by instructions. They refer to the scripts in `buildroot/bin` and the `Makefile` so the procedures stay accurate as the scripts evolve.\n\nAll commands assume the current directory is the repository root.\n\n| Skill | Purpose |\n| --- | --- |\n| [marlin-build-test](marlin-build-test/SKILL.md) | Build a board environment, apply test configs, run CI-style tests |\n| [marlin-config-options](marlin-config-options/SKILL.md) | Find, enable, disable, and set configuration options from the command line |\n| [marlin-code-style](marlin-code-style/SKILL.md) | Follow the Marlin coding standards and indentation; leave existing code unformatted |\n| [marlin-pr-workflow](marlin-pr-workflow/SKILL.md) | Prepare a focused, reviewable Pull Request |\n| [marlin-solving-bugs](marlin-solving-bugs/SKILL.md) | Investigate, isolate, fix, and verify a bug report |\n| [marlin-extui-backends](marlin-extui-backends/SKILL.md) | Add or change ExtUI display backends and the ExtUI API |\n| [marlin-unit-tests](marlin-unit-tests/SKILL.md) | Write and run native unit tests; macOS GCC setup |\n| [marlin-codebase-navigation](marlin-codebase-navigation/SKILL.md) | Find definitions, uses, and flows in the source with git and grep |\n| [marlin-hal-work](marlin-hal-work/SKILL.md) | Add a board, extend a HAL, or create a new HAL |\n| [marlin-fork-harvesting](marlin-fork-harvesting/SKILL.md) | Evaluate a Marlin fork and port its features cleanly |\n\n## Adding a skill\n\n- Make a folder `buildroot/skills/<skill-name>/` with a `SKILL.md`.\n- Start `description` with when to use the skill. Keep the skill short and procedural.\n- Refer to scripts in `buildroot/bin` instead of duplicating their logic, and verify every command you document.\n- Skills are for all contributors. Don't include personal paths or maintainer-only procedures.\n- Add the skill to the table above.\n","buildroot/skills/marlin-solving-bugs/SKILL.md":"---\nname: marlin-solving-bugs\ndescription: Use when investigating and fixing a Marlin bug report, from reproducing it to a verified patch. Covers triage, finding the code path, isolating the cause, a minimal root-cause fix, and verification.\n---\n\n# Fix a Marlin Bug\n\nFind the root cause before you write any fix. Marlin has many HALs and a huge number of configuration combinations, and there is no hardware-in-the-loop CI, so the evidence you gather matters more than the size of the patch.\n\n## 1. Triage the report\n\n- Get the exact Marlin version or commit, the board, and `Configuration.h` / `Configuration_adv.h` (a `marlin_config.json` or a Configurations ZIP is best).\n- Find the first version that failed, and the last that worked. If the reporter can test, ask for a bisect (`git bisect`) between those points.\n- Check whether `bugfix-2.1.x` still has the bug. Fixes land there first, and old reports are often already fixed.\n- Files move between releases. Search for the current location of any path in an old report: `ultralcd.*` became `marlinui.*`, and `Conditionals_post.h` became `Conditionals-5-post.h`.\n\n## 2. Find the code path\n\n    git grep -n \"OPTION_NAME\" -- Marlin/src            # who uses an option\n    git log --oneline -20 -- Marlin/src/module/motion.cpp\n    git log -S\"suspicious_string\" --oneline -- Marlin/src\n    git log --oneline 2.1.2..2.1.3 -- Marlin/src/module/probe.cpp\n\n- G-code handlers are in `Marlin/src/gcode/`, with one file per command or small group.\n- Trace which `#if` paths the reporter's config enables. A bug often appears only for one combination of options.\n- A regression usually has a specific commit. Read its diff and its PR discussion to learn what it was meant to do.\n\n## 3. Isolate the cause\n\n- Reproduce on the smallest config you can, using `buildroot/bin/opt_*` to set only the needed options (skill: marlin-config-options).\n- Test as much as possible in the native simulator ([MarlinSimUI](https://github.com/MarlinFirmware/MarlinSimUI)) or a unit test, so you can run and re-run the scenario.\n- Change one thing at a time and keep notes. A temporary `SERIAL_ECHOLNPGM` can help, but remove every debug statement before committing.\n- State your hypothesis and the evidence before coding the fix. If you can, show that reverting the suspect change removes the symptom.\n\n## 4. Fix the root cause\n\n- Fix the wrong logic itself. Don't add workarounds elsewhere that hide the symptom.\n- Make the smallest diff that does it. No reformatting, no unrelated cleanup (skill: marlin-code-style).\n- Think about every configuration the changed code is compiled in. Build with the relevant options both on and off.\n\nCommon causes:\n\n| Symptom | Look for |\n| --- | --- |\n| Wrong distance or speed | mm vs steps vs mm/s units; missing conversion |\n| Works with one option, breaks with another | A missing `ENABLED()` guard, or the wrong layer in `Conditionals-*.h` |\n| Wrong axis | `X_AXIS` used where `Z_AXIS` was meant; macro for the wrong axis |\n| Fails only on AVR | Integer overflow (use `uint32_t`), `double`, RAM and flash limits |\n| Fails only on one board | HAL differences: timers, serial buffering, pin multiplexing |\n| Position drift after an event | A reset of `current_position` or the planner position without `sync_plan_position()` |\n| Lost settings after upgrade | A `settings.cpp` layout change without a version bump |\n\n## 5. Verify\n\n- Build with `mftest` (skill: marlin-build-test). At least one AVR (tight flash) and one 32-bit environment for common code.\n- Run the reporter's scenario again, not just a build. A passing build is not proof the bug is fixed.\n- If the logic is testable without hardware, add a unit test that fails before your fix and passes after.\n- If you add a config interaction, add a check to `Marlin/src/inc/SanityCheck.h`.\n\n## 6. Submit\n\nFollow skill marlin-pr-workflow. In the PR description, say what the root cause was, how you reproduced it, and what you tested (including on what hardware, if any). Reference the issue in the description, not the title. Don't claim the issue is closed until the reporter or another tester confirms it on hardware.\n\n## Pitfalls\n\n- **Debug code left in.** `#define DEBUG_OUT 1`, `DEBUG_ECHOLNPGM`, and temporary prints must not be in the final diff. Check with `git diff | grep -n DEBUG_`.\n- **Stale `case` labels.** After an enum value is removed, a `case REMOVED_VALUE:` can survive a rebase. Grep for removed names when a PR touches enums.\n- **Shared string buffers.** The `numtostr` family (`ftostr*`, `i16tostr*`) writes into one shared buffer. Never pass one function's result into another, and don't hold a pointer across calls.\n- **Variables declared outside the guard of their only uses** cause `-Wunused-variable`, and CI treats warnings as errors. Put the declaration inside the same guard.\n- **A function whose signature uses a type from a guarded header** needs its definition guarded the same way as its declaration.\n- **Verify a premise before relying on it.** For example, `IS_SCARA` also covers TPARA, so grep the macro definition before saying a path doesn't apply to a variant.\n\n## Reading a failed CI run\n\n    gh run view <run_id> --repo MarlinFirmware/Marlin --json jobs -q '.jobs[] | select(.conclusion==\"failure\") | [.databaseId, .name] | @tsv'\n    gh run view <run_id> --job <job_id> --log | grep -E \"error:|FAILED|undefined\" | head -30\n\nA job that failed in under 30 seconds is nearly always a compile or sanity-check error. Longer ones are often timeouts.\n"},"files":{"buildroot/skills/marlin-config-options/SKILL.md":"---\nname: marlin-config-options\ndescription: Use when you need to find, enable, disable, or set Marlin configuration options (Configuration.h / Configuration_adv.h) from the command line. Wraps buildroot/bin/opt_find, opt_enable, opt_disable, opt_set, and opt_add.\n---\n\n# Marlin Configuration Options from the Command Line\n\nThese scripts edit `Marlin/Configuration.h` and `Marlin/Configuration_adv.h` in place. Run them from the repo root. Commit or stash your own edits first so the changes are easy to revert, or run `buildroot/bin/restore_configs` afterward (which does a `git checkout` of the config files, discarding uncommitted edits to them).\n\n## Find\n\n    buildroot/bin/opt_find LIN_ADV         # enabled or disabled options matching the name\n    buildroot/bin/opt_find -e BABYSTEP     # only enabled\n    buildroot/bin/opt_find -d BABYSTEP     # only disabled\n\n## Enable / disable\n\n    buildroot/bin/opt_enable LIN_ADVANCE ADAPTIVE_STEP_SMOOTHING\n    buildroot/bin/opt_disable ADAPTIVE_STEP_SMOOTHING\n\n`opt_enable` exits with an error if an option can't be found.\n\n## Set a value\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_RAMPS_14_EFB EXTRUDERS 2\n\nTakes `<name> <value>` pairs. An option that does not exist is added to `Configuration.h`.\n\n## Use example configurations\n\n    buildroot/bin/use_example_configs Creality/CR-10/CrealityV1\n\nFetches the named example from the Configurations repo (branch matched to your working branch, otherwise `bugfix-2.1.x`). Use `release-2.0.9.4:Creality/CR-10/CrealityV1` to pick a branch or tag.\n\n## Tips\n\n- Option names are matched exactly by enable/disable/set. Use `opt_find` first.\n- Some options need others (see `Marlin/src/inc/SanityCheck.h`). A build error from a sanity check tells you what is missing.\n- The CI test files in `buildroot/tests/` use these same operations. Read a few as examples.\n","buildroot/skills/marlin-build-test/SKILL.md":"---\nname: marlin-build-test\ndescription: Use when you need to compile Marlin for a board, apply a CI test configuration, or run the build tests locally. Wraps buildroot/bin/mftest, mfenvs, run_tests, and the Makefile targets.\n---\n\n# Build and Test Marlin\n\nRequires PlatformIO (`pio`) in your `PATH`. Run everything from the repo root.\n\n## Build the configured board\n\n1. Set `MOTHERBOARD` in `Marlin/Configuration.h` and set up your options.\n2. List the PlatformIO environments for the board: `buildroot/bin/mfenvs`\n3. Build the auto-detected environment: `buildroot/bin/mftest -a` (rebuild with `-r`, silent with `-s`).\n4. Or build one environment directly: `pio run -e <env>`\n\n## Apply a CI test config and build it\n\nTests live in `buildroot/tests/<env>/config-NN.ini` and are applied with the `opt_*` tools.\n\n- Interactive menu: `buildroot/bin/mftest`\n- Specific test: `buildroot/bin/mftest -t mega2560 -n 5 -y` (`-t` env name or shortcut, `-n` test index, `-y` build without asking)\n- Start from default configs first: add `-d`\n- Only emit the config changes without building: add `-i`\n- Use the Docker/make method: add `-m`\n\nShortcuts for `-t`: `tree due esp lin lp8 lp9 m128 mega stm f1 f4 f7 s6 teensy t31 t35 t40`.\n\n## Add tests for a new feature or option\n\nA PR that adds a feature or option should add tests, so CI keeps it building and working.\n\n**Build test** (`buildroot/tests/<env>/config-NN.ini`)\n\n1. Pick the env directory that matches a board and platform that can build the feature. Use `buildroot/bin/mfenvs` or the existing directories under `buildroot/tests`.\n2. Prefer adding the option to an existing `config-NN.ini` with a similar feature set. Add a new file only for a combination that no existing test covers. Use the next free number.\n3. Each file begins with a header (`# Test: <env>/config-NN.ini`, then a one-line description of the main options) and a `[config:base]` section. Start from an example with `ini_use_config = example/...`, or leave it out for the defaults.\n4. Set options as `name = value` or `name = on`. Test the feature enabled along with the options it depends on, and with the dependent sub-options.\n5. Run just your test: `buildroot/bin/mftest -t <env> -n <index> -y` or `make tests-single-local TEST_TARGET=<env> ONLY_TEST=<index>`, where `<index>` is the test's position in file order (1-based) and normally matches `NN`.\n6. Keep the number of tests small. Every test adds to CI time.\n\n**Unit test** (when the logic can be checked without hardware)\n\nUnit tests run natively on the `linux_native_test` environment. Good candidates are parsing, math, conversions, state machines and G-code handling. Anything that needs real pins, timers, or devices isn't a good candidate. See `test/README.md`.\n\n1. Add `Marlin/tests/<area>/test_<name>.cpp`. Copy the license header and the `MARLIN_TEST(group, name)` macros from an existing test such as `Marlin/tests/feature/test_runout.cpp`.\n2. Wrap the file's contents in `#if ENABLED(FEATURE)` so it compiles only for configs that enable it.\n3. If no existing config in `test/*.ini` enables your feature, add one (`test/NNN-name.ini`). It must use `motherboard = BOARD_SIMULATED` and include only the options needed to pass the sanity checks.\n4. Run: `make unit-test-all-local`, or one config with `make unit-test-single-local UNIT_TEST_CONFIG=<name>`. (There is a `-docker` variant of each.)\n\n## Run the tests like CI does\n\n- One environment: `make tests-single-local TEST_TARGET=mega2560`\n- One test of it: `make tests-single-local TEST_TARGET=mega2560 ONLY_TEST=2`\n- Everything (slow): `make tests-all-local`\n- In Docker: `make tests-single-local-docker TEST_TARGET=mega2560` (see `make help`)\n- Unit tests: `make unit-test-all-local`\n\nThe test runner restores the configs between tests and may `git reset --hard` (see `GIT_RESET_HARD` in `make help`), so commit or stash your work first.\n\n## Restore configuration files\n\nAfter testing, run `buildroot/bin/restore_configs`. It deletes generated files (`marlin_config.json`, `_Bootscreen.h`, `_Statusscreen.h`) and does a `git checkout` of the configuration files, discarding any uncommitted edits to them. `restore_configs -d` instead loads the default example configs.\n\n## Choosing what to build\n\n- Build at least one environment each for any HAL or board family you changed.\n- A change to common code deserves an AVR build (tight flash) and a 32-bit build.\n- For features, build with the feature both enabled and disabled.\n- New feature or option: add a build test and, where it makes sense, a unit test (see above).\n\n## Report results\n\nState exactly which envs and tests you built. Do not claim a pass that you did not see.\n","buildroot/skills/marlin-code-style/SKILL.md":"---\nname: marlin-code-style\ndescription: Use before committing C/C++ changes to Marlin, to match project conventions and indentation in new code without reformatting existing code.\n---\n\n# Marlin Code Style\n\nFull standards: https://marlinfw.org/docs/development/coding_standards.html\n\n## Rules of thumb\n\n- Match the file you are editing: indentation (2 spaces, no tabs), brace placement, spacing, naming.\n- Code inside preprocessor blocks (`#if` / `#else` / `#endif`) is indented one level per nesting depth, the same as code inside braces. Copy how nearby code does it.\n- Use `ENABLED(FEATURE)` / `DISABLED(FEATURE)` for options, and `#if HAS_...` for derived capabilities.\n- Keep optional code behind its feature flag so it costs nothing when disabled.\n- Code must build on 8-bit AVR as well as 32-bit targets. Avoid unnecessary `double`, `String`, and heap allocation.\n- Comments should say why, concisely. End every file with a newline. No trailing whitespace.\n\n## Don't reformat existing code\n\n- Existing code may have ad hoc alignment and spacing (aligned columns, grouped one-liners). Leave it alone.\n- Don't re-indent, re-wrap, or re-space lines you aren't otherwise changing. Keep diffs small and focused.\n- Exception: It's good to keep related eol comments aligned.\n- Review `git diff` before committing and revert any whitespace-only churn.\n\n## Auto-formatters\n\nDon't run the auto-formatter on existing files. `buildroot/bin/format_code` and `uncrust` (uncrustify) get close to the Marlin style but don't match it exactly, and will produce unwanted churn. If you use one, apply it only to a scratch copy to see suggestions, then hand-apply what is correct to your own new lines.\n","buildroot/skills/marlin-codebase-navigation/SKILL.md":"---\nname: marlin-codebase-navigation\ndescription: Use when you need to find where something is defined, what uses it, or how a command or setting flows through Marlin's source. Uses only git, grep, and the repo layout, with optional tools as extras.\n---\n\n# Navigate the Marlin Source\n\nMarlin is a large C++ tree where most code is compiled only under certain options. These steps work with nothing but `git` and a shell. Better tools are listed at the end as optional.\n\nFor the layout, singletons, and processing pipeline, read `AGENTS.md` (sections 2 to 4 and 10) first. This skill covers how to search, and where the common things live.\n\n## Where things live\n\n| Looking for | Look in |\n| --- | --- |\n| A G-code command `Mnnn` / `Gnnn` | `git grep -n \"GcodeSuite::M913\"`. Handlers are in `Marlin/src/gcode/<group>/`, and some files hold several (`M911-M914.cpp`). The dispatch `switch` is in `Marlin/src/gcode/gcode.cpp`, and declarations are in `gcode/gcode.h` |\n| What runs each loop | `setup()` and `loop()` in `Marlin/src/MarlinCore.cpp` |\n| Motion, planner, stepper ISR | `Marlin/src/module/` (`motion`, `planner`, `stepper`) |\n| Temperature, PID, MPC | `Marlin/src/module/temperature.cpp` |\n| Optional feature code | `Marlin/src/feature/` and `gcode/feature/` |\n| An option's derived flags (`HAS_*`) | `Marlin/src/inc/Conditionals-*.h` |\n| Why a config combination is rejected | `Marlin/src/inc/SanityCheck.h`; renamed options are in `Changes.h` |\n| A board's pins | `Marlin/src/pins/<family>/pins_<BOARD>.h`; the `#elif MB(...)` chain is in `pins/pins.h`; the ID is in `core/boards.h` |\n| Platform-specific code | `Marlin/src/HAL/<FAMILY>/`, and that folder's `AGENTS.md` |\n| An LCD string | `Marlin/src/lcd/language/language_en.h` (`MSG_*`) |\n| A menu screen | `Marlin/src/lcd/menu/menu_*.cpp` |\n| A serial or touch display | `Marlin/src/lcd/extui/` |\n| Which source folders a build includes | `ini/features.ini` and `ini/*.ini` |\n| A CI test config | `buildroot/tests/<env>/config-NN.ini` |\n\n## Search recipes\n\n    git grep -n \"OPTION_NAME\" -- Marlin                  # every use, including config comments\n    git grep -n \"SOME_OPTION\" -- 'Marlin/src/inc/*'      # where it is derived or validated\n    git grep -nE \"\\bfunction_name\\(\" -- Marlin/src       # calls and definitions\n    git grep -n \"STR_SOMETHING\\|MSG_SOMETHING\" -- Marlin/src/lcd\n    git log -S\"suspicious_string\" --oneline -- Marlin/src   # when it was introduced or removed\n    git log --oneline -20 -- Marlin/src/module/motion.cpp   # recent changes to a file\n    git blame -L 120,160 Marlin/src/module/motion.cpp       # who changed these lines, then read that PR\n\n## Tracing a setting or command\n\n1. Find the option in `Marlin/Configuration.h` or `Configuration_adv.h`.\n2. `git grep` it under `Marlin/src/inc` to see what it enables and what it conflicts with.\n3. `git grep` it under the rest of `Marlin/src` to find the code it gates.\n4. For a G-code, go from the `case` in `gcode/gcode.cpp` to its handler, then follow the calls into `module/`.\n5. If a symbol seems to have no users, check for macro use (`TERN`, `ENABLED`) and for different spellings under other options before deciding it is dead.\n\n## Pitfalls\n\n- **Most code is conditional.** A match inside `#if ENABLED(X)` is not compiled unless `X` is on. Read the enclosing `#if`s, and build with the option on and off.\n- **Macros hide calls.** `SERIAL_ECHO*`, `TERN*`, `LOOP_*`, and `HAL_PATH` includes do not look like ordinary function calls, so search for the macro name too.\n- **Old issues and forks use old paths.** `ultralcd.*` is now `marlinui.*`, and `Conditionals_post.h` was split into the numbered files. Search the current name before applying advice from an old thread.\n- **Line numbers drift.** Quote a symbol and a file, not only a line number, in a PR or comment.\n- **IDE diagnostics can mislead.** Marlin builds with GCC. A Clang-based language server often reports errors on headers that build fine. Trust the build (skill: marlin-build-test).\n\n## Optional tools\n\nNone of these are required, and none replace reading the code.\n\n- `rg` (ripgrep) is faster than `git grep` on the whole tree and handles the same patterns.\n- Your AI tool's own search (for example Claude Code's file and content search, or a code-exploration sub-agent) works well for broad questions such as \"where is homing decided\". Check its answer with `git grep`.\n- A symbol index such as `universal-ctags` helps with jump-to-definition in an editor. Because of the `#if` blocks it can show several definitions of one name.\n- A code-knowledge-graph MCP server (for example codebase-memory-mcp) can locate definitions, line ranges, and function size from a natural-language query. In testing on Marlin, its call-graph results were incomplete: callers came back at file level and macro-dispatched code had no callees. Use it to locate code and `git grep` to find every use. It indexes a snapshot of the checked-out tree, so re-index after switching branches.\n- If you try a language-server plugin, expect noise from the heavy preprocessor use and keep a real build as the final check.\n","buildroot/skills/marlin-fork-harvesting/SKILL.md":"---\nname: marlin-fork-harvesting\ndescription: Use when evaluating a GitHub fork of Marlin (a vendor's or a community member's) and porting a feature, board, or display driver into mainline as a clean, reviewable PR.\n---\n\n# Port a Feature from a Marlin Fork\n\nForks often hold support for hardware that mainline lacks. Their code can't be merged as-is: it is based on an older Marlin, may restructure the HALs, and may not follow the project style. Port the part that belongs in mainline, and port it cleanly.\n\nCheck the fork's license and keep attribution. Say in the PR where the code came from and credit the author.\n\n## 1. Decide whether it is worth porting\n\n| Question | How to check |\n| --- | --- |\n| Is it maintained? | Last push date |\n| Is it really Marlin? | It has `Marlin/src/` |\n| Is it already in mainline? | Search the Marlin PRs and `git log` for the board or feature |\n| Does it add hardware or features mainline lacks? | See step 3 |\n| Is the quality reasonable? | Read two or three key files |\n\nThe vendor's own repo is a secondary source. It is often a frozen snapshot from the product launch, based on an old Marlin, with stale board IDs and an outdated layout. Use it only to cross-check pin assignments and names, not as a port base.\n\n## 2. Add the fork as a remote\n\n    git remote add <author-slug> https://github.com/<Author>/Marlin\n    git fetch <author-slug>\n    git branch -r | grep <author-slug>\n\n## 3. Find only the relevant changes\n\nA fork that diverged long ago shows thousands of changed files, nearly all of it mainline drift. Filter to what matters:\n\n    git log --oneline upstream/bugfix-2.1.x..<slug>/<branch> | wc -l\n    git diff --name-only upstream/bugfix-2.1.x <slug>/<branch> | grep -iE '<machine>|src/pins/|src/HAL/|src/lcd/'\n    git show <slug>/<branch>:Marlin/Configuration.h | grep -E 'MOTHERBOARD|MACHINE_NAME'\n\nCheck each of these:\n\n- **New HAL?** If the board needs an MCU family that mainline doesn't support, decide with the maintainers whether the HAL is part of this PR or a separate one before you write code.\n- **Board entry?** Check `Marlin/src/core/boards.h`. A new board needs a `boards.h` entry, a `pins.h` entry, and a pins file.\n- **UI code?** Large display-driver or UI changes should go in a separate PR from the core feature.\n\n## 4. Port, don't cherry-pick\n\nMake a clean-room port in most cases. Read the fork's code, then write it in mainline's style on a fresh branch:\n\n    git checkout -b <topic> upstream/bugfix-2.1.x\n\nCherry-pick only when the commits are linear, clean, and conflict-free.\n\nNever port a fork's HAL restructuring (deleted platforms, renamed folders, moved files). Take the target board's pins file and the genuinely new driver or feature.\n\n### Rules\n\n1. **Use mainline's names and layout.** A fork's folder names may differ (for example `hc32f46x/` vs mainline `hc32f4/`). Check `ls Marlin/src/pins/` and follow it.\n2. **Board IDs must be verified against `boards.h`.** A fork's ID is often already taken in mainline. Use the next free ID in the right architecture range:\n\n       grep '#define BOARD_' Marlin/src/core/boards.h | awk '{print $3}' | sort -n\n\n   Keep the `boards.h` order the same as the `#elif MB()` order in `pins.h`.\n\n3. **Config defaults** go in the `Conditionals-*.h` layer. `SanityCheck.h` only validates currently-valid combinations.\n4. **Renamed or removed options** get an `#error` in `Marlin/src/inc/Changes.h` that points to the replacement.\n5. **Old G-codes and M-codes** that the port replaces should keep working as shims where reasonable.\n6. **EEPROM data** follows the struct pattern in `settings.cpp`, with a version bump.\n7. **Don't copy old APIs.** Fork code often uses names removed from mainline. Fix each compile error against current headers rather than adding compatibility macros.\n\n### New ExtUI display drivers\n\nSee skill marlin-extui-backends. In particular:\n\n- Add the driver folder to `ini/features.ini`. If it is missing, the whole driver is silently excluded and every ExtUI callback is \"undefined\".\n- Add the new LCD define to the lists in `Conditionals-2-LCD.h`, and add its `SanityCheck.h` entries.\n- Compare the fork's handlers with the current `ui_api.h` (renamed `onMediaMounted`, handlers with new argument types, newer handlers such as `onSurviveInKilled`).\n\n### Per-HAL notes\n\nRead `Marlin/src/HAL/<FAMILY>/AGENTS.md`. For example, a HAL may lack an EEPROM backend the fork relied on (`PersistentStore::*` undefined at link), or need a board-defined crystal frequency.\n\n## 5. Build and test\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_<NAME>\n    buildroot/bin/mftest -a -n1 -y\n    buildroot/bin/restore_configs\n\n- Build with the feature disabled and enabled.\n- Add a test config for new options (skill: marlin-build-test). A test config must satisfy the sanity checks, for example `NOZZLE_AS_PROBE` needs a zero probe offset, and bed leveling with a physical probe needs `Z_SAFE_HOMING`.\n- Say honestly in the PR what you built, and that no one has tested on hardware if that is so.\n\n## 6. Commit and PR\n\nSplit it: core pins and board first, display driver second, extra features after that (skill: marlin-pr-workflow). Credit the fork's author in the PR description.\n","buildroot/skills/marlin-extui-backends/SKILL.md":"---\nname: marlin-extui-backends\ndescription: Use when adding or changing an ExtUI display backend (DGUS, Anycubic, Nextion, Malyan, FTDI EVE, Neptune 3, IA Creality, serial panels) or the ExtUI API in ui_api.h / ui_api.cpp.\n---\n\n# Marlin ExtUI Backends\n\nExtUI (`Marlin/src/lcd/extui/ui_api.{h,cpp}`) is the API used by serial and touch displays. Backends live in subfolders of `Marlin/src/lcd/extui/`. `example/example.cpp` is the template for a new one.\n\n## Rules\n\n1. **Event handlers (`ExtUI::on*()`) have weak, empty defaults in `ui_api.cpp`.** A backend defines only the handlers it uses. Don't add empty `{}` stubs to a backend, and delete them if you see them. A new handler declared in `ui_api.h` gets exactly one weak empty default in the `WEAK_EXTUI` block in `ui_api.cpp`, inside the same `#if` guard as its declaration. No backend needs to change.\n2. **API functions (`get*` / `set*`) are not event handlers.** They get one real implementation in `ui_api.cpp`, next to the matching getter, and are never weak or inside a backend. A setter moved into the backends leaves empty stubs that silently do nothing.\n3. **Keep the `ExtUI` namespace flat.** Don't use the `using namespace Base` override pattern from `language_*.h`: `ui_api.h` already declares every handler in `ExtUI`, and name hiding breaks overloads such as `onUserConfirmRequired` and `onMeshUpdate`.\n4. **Backends keep to their own folder.** Add the folder to `ini/features.ini` (`build_src_filter=+<src/lcd/extui/<folder>>`), or it will be silently excluded and the link fails with every callback undefined.\n5. **Wire the new LCD define in `Conditionals-2-LCD.h`** (the `IS_EXTUI` list, the `LCD_IS_SERIAL_HOST` list if it is a serial display, and the \"only one LCD\" count), and add its checks to `SanityCheck.h`. Copy a similar backend's entries.\n6. **Handlers must match the current `ui_api.h` signatures.** Backends ported from forks often use renamed or removed handlers. Diff against the full callback list in `ui_api.h` before building.\n7. **After a bulk deletion, tidy up.** Remove runs of blank lines, a blank line directly after `#if` or before `#endif`, orphaned comments, and emptied `#if` blocks. Diff against the base and check that the only `+` lines are the ones you intended.\n\n## A panel that speaks G-code (PanelDue and similar)\n\nTreat it as an ExtUI backend that owns `LCD_SERIAL`, with the whole protocol inside the backend. Add it to `LCD_IS_SERIAL_HOST` (which auto-assigns `LCD_SERIAL_PORT`) rather than routing it through `SERIAL_PORT_2`.\n\n- `LCD_SERIAL` is never a G-code source. The queue reads only `NUM_SERIAL` ports, and `SERIAL_ECHO*` never reaches `LCD_SERIAL`. The backend must call `LCD_SERIAL.begin(LCD_BAUDRATE)` in `onStartup()`, read lines in `onIdle()`, and write replies to `LCD_SERIAL` itself, as Malyan does.\n- Answer status queries directly from `onIdle()`, never through the queue. `idle()` keeps running during `G28`, `M109`, and SD prints, so the panel stays connected while Marlin is busy.\n- Run other commands by injection. `queue.inject()` overwrites the single `injected_commands` buffer (64 bytes), so keep your own FIFO. Hand over whole lines only when `queue.injected_commands[0] == 0`. Send a line too long for the buffer with `queue.enqueue_one()` and retry if that fails.\n- Apply temperature, fan, speed, and flow changes with the ExtUI setters, so they take effect even during a blocking `M109`.\n- Buffer replies and drain them with `LCD_SERIAL_TX_BUFFER_FREE()` (`SERIAL_GET_TX_BUFFER_FREE()` on ESP32 and RP2040). A 1 KB reply takes about 180 ms at 57600 baud, which is too long to block `idle()` during a print.\n- Keep each JSON or status reply on one line. Clients such as PanelDue discard a partial object when they see a newline.\n- If the panel has its own buzzer, add it to the `USE_MARLINUI_BUZZER` list in `Conditionals-5-post.h` and define `MarlinUI::buzz()` in the backend. Without a `BEEPER_PIN`, `HAS_SOUND` is off and `M300` isn't compiled.\n\n## Verify\n\n1. Build every backend the change touches. Find the tests that cover them:\n\n       grep -rliE \"elegoo_neptune_3_tft|dwin_lcd_proui|touch_ui_ftdi_eve|extui_example|dgus_lcd_ui\" buildroot/tests\n\n   Build one with `buildroot/bin/mftest -t <env> -n <N> -y`.\n\n2. Backends with no CI test need a manual build. Use an STM32 board, not AVR RAMPS, because serial LCDs on RAMPS fail on a pin conflict with Serial Port 3:\n\n       buildroot/bin/restore_configs\n       buildroot/bin/opt_set MOTHERBOARD BOARD_BTT_SKR_PRO_V1_1 SERIAL_PORT -1 LCD_SERIAL_PORT 1\n       buildroot/bin/opt_enable EEPROM_SETTINGS SDSUPPORT AUTO_BED_LEVELING_BILINEAR FIX_MOUNTED_PROBE Z_SAFE_HOMING PIDTEMPBED\n       buildroot/bin/opt_enable MALYAN_LCD            # or the backend's own option(s)\n       buildroot/bin/mftest -a -n1 -y\n\n   Some backends need extra options (for example `LCD_BED_TRAMMING` and `BABYSTEPPING` for Anycubic, `CLASSIC_JERK` for DGUS E3S1PRO). Read the first error.\n\n3. Judge each build by its log: count `[SUCCESS]` and `error:`. Don't pass `-s` to `mftest`, which hides the log. Run builds one at a time in a single clone, since parallel builds collide on the `.pio` state.\n4. Confirm an override really happened. With the ARM toolchain's `nm`:\n\n       arm-none-eabi-nm -C .pio/build/<env>/firmware.elf | grep -E ' ExtUI::on[A-Za-z]+\\('\n\n   `T` is the backend's own definition, and `W` is the weak default. A handler you defined that still shows `W` wasn't compiled, so check the guards around it.\n\n5. Run `buildroot/bin/restore_configs` between builds and at the end. `git status --short` should then show only your source edits.\n6. For a serial-panel backend, also run it end to end in the simulator with a scripted panel emulator. Check that replies are valid single lines, that the panel is still answered while Marlin is busy, and that each translated command takes effect.\n","buildroot/skills/marlin-hal-work/SKILL.md":"---\nname: marlin-hal-work\ndescription: Use when adding a board to an existing HAL, extending a HAL, creating a new HAL, or debugging HAL-specific code. Includes the board checklist, comparing HALs, and verifying framework APIs.\n---\n\n# Marlin HAL and Board Work\n\nEach HAL folder `Marlin/src/HAL/<FAMILY>/` has its own `AGENTS.md` with the build and test loop and the gotchas for that family. Read it first.\n\nThree levels of work, from most to least common:\n\n1. **Add a board** to an existing HAL.\n2. **Extend a HAL** with an MCU variant.\n3. **Create a new HAL** for a new architecture.\n\n## Compare, don't generalize\n\nDon't assume one HAL's behavior holds for another. Timers, ISR entry, serial buffering, and ADC differ a lot. To learn the contract, compare three representatives: a simple one (AVR), a mid one (LPC1768), and a complex one (STM32 or ESP32).\n\n    ls Marlin/src/HAL/*/                      # a file in 10+ HALs is probably required\n    head -80 Marlin/src/HAL/{AVR,LPC1768,STM32,RP2040}/timers.h\n    head -50 Marlin/src/HAL/{AVR,LPC1768,STM32,RP2040}/fastio.h\n\n`Marlin/src/HAL/shared/` holds what every HAL inherits (`Marduino.h`, `serial_ports.h`, `eeprom_api.h`, ...). `Marlin/src/HAL/platforms.h` is the single dispatch point: a wrong detection macro loads the wrong HAL, and the failure is confusing.\n\n## Adding a board\n\n| Step | File | Action |\n| --- | --- | --- |\n| Pins | `Marlin/src/pins/<arch>/pins_<BOARD>.h` | All `*_PIN` defines. Use a `_common.h` file when variants share a pinout |\n| Board ID | `Marlin/src/core/boards.h` | `#define BOARD_<NAME> <ID>` in the right architecture range, highest ID + 1 |\n| Routing | `Marlin/src/pins/pins.h` | `#elif MB(<NAME>)` in the same relative position as in `boards.h` |\n| Environment | `ini/<arch>.ini` | Add an `[env:...]` that `extends` a similar one |\n| Validation | `Marlin/src/pins/<arch>/env_validate.h` | Only if the family has a unique constraint |\n| CI test | `buildroot/tests/<env>/` | Optional |\n\nDo not edit `config.ini` for a board.\n\nConventions:\n\n- Letters between `pins_` and `.h` are UPPERCASE. The `_common` suffix is lowercase. Prefix the manufacturer's name if the board name isn't distinctive.\n- The `#define` order in `boards.h` must match the `#elif MB()` order in `pins.h`.\n- Set `BOARD_INFO_NAME` (and optionally `DEFAULT_MACHINE_NAME`) in the board's pins file.\n- Use `#ifndef` guards around pins that variants may override.\n- The EEPROM backend is chosen per board: most boards define `FLASH_EEPROM_EMULATION` or `SDCARD_EEPROM_EMULATION` in the pins file. If none is enabled you get an `undefined reference to PersistentStore::*` at link.\n- Link the schematic or source of the pin data in a comment, if there is one.\n\n### Verifying pin and UART assignments\n\nCheck open sources before saying a pin can't be verified without hardware. Cross-check at least two:\n\n1. Klipper configs (`Klipper3d/klipper/config/printer-<vendor>-<model>*.cfg`, `generic-<board>.cfg`). They list step/dir/enable/heater/fan pins, and the MCU serial pins in comments.\n2. Vendor or community Marlin forks: `pins_*.h`, the variant's `variant.h` and `PeripheralPins.c`, and the ini env's `build_flags`.\n3. Elimination: a UART has only a few alternate-function pin pairs. If the others are used by steppers or heaters, the one left is the answer.\n\nSTM32duino uses `PIN_SERIALn_RX/TX` if defined. Otherwise it takes the first matching entry in the variant's pin map. A board whose UART is on a later pair needs `-DPIN_SERIALn_RX=...` and `-DPIN_SERIALn_TX=...` in its env.\n\n### Validate with mftest\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_<NAME>\n    buildroot/bin/mftest -a -n1 -y\n\nRaw `pio run -e <env>` reads whatever `Configuration.h` was last generated, and can report a phantom `Build environment is incompatible with BOARD_...`. Trust `mftest` and don't chase a pio-only \"incompatible\" error. A passing build only shows the files are structurally sound. Say so in the PR if no one has tested the board.\n\n## Creating a new HAL\n\nThe minimum set that must exist to compile:\n\n- A branch in `platforms.h`\n- `HAL.h` and `HAL.cpp` (the `MarlinHAL` class)\n- `fastio.h`\n- `timers.h` and `timers.cpp`\n- `MarlinSerial.h` and `MarlinSerial.cpp`\n- `inc/Conditionals-2-LCD.h` and `inc/SanityCheck.h`, plus the other files in `inc/`, which can be empty (`#pragma once`) but must exist\n\nUse a recent HAL such as HC32 as the layout reference. Everything else fills in as features are enabled.\n\n## Verify against the real framework\n\nCode written without the vendor framework in view tends to invent names borrowed from another platform (for example `GPIO_TypeDef`, `BSRR`, `Pin_TypeDef`). The installed framework (`~/.platformio/packages/framework-*`) is ground truth: grep it before writing or \"fixing\" any peripheral symbol. A symbol that isn't there is a HAL bug, not a missing dependency.\n\n## Pitfalls\n\n- A HAL header missing `#include \"../../inc/MarlinConfig.h\"` makes shared files report `FSTR_P`, `PGM_P`, `millis_t`, or `pin_t` undefined. Fix the one include, not the cascade. Files in `HAL/<ARCH>/<sub>/` need `../../../inc/MarlinConfig.h`.\n- A timer ISR macro with no vector or NVIC wiring compiles but never fires.\n- `MF_TIMER_STEP`, `MF_TIMER_TEMP`, and `MF_TIMER_PWM` must not collide with each other or with platform-reserved timers.\n- `CRITICAL_SECTION_START/END` must nest (save and restore the previous interrupt state). Don't use bare `__disable_irq()` / `__enable_irq()`.\n- `pin_t` must match the platform's GPIO numbering.\n- `SERIAL_PORT` numbering differs between HALs (AVR 0-3, STM32 and RP2040 -1/0/1).\n- A serial adapter class must subclass the real core serial class. Forward-declaring it gives \"incomplete type\" errors.\n- Some cores lack the bare `Serial` global or `spiBegin()`, which Marlin code calls.\n- Vendored framework files may use CRLF. Some editors and tools turn an escape such as `\"\\r\\n\"` into a real control character, so use hex constants like `0x0D`.\n- Don't remove or break the AVR HAL. It is the reference implementation.\n- Some boards must keep the base AVR flash and RAM budget. Don't let the base build grow.\n","buildroot/skills/marlin-pr-workflow/SKILL.md":"---\nname: marlin-pr-workflow\ndescription: Use when preparing, splitting, or submitting a Marlin Pull Request. Covers PR size, branch, commit messages, rebase, and the PR template.\n---\n\n# Marlin Pull Request Workflow\n\nPolicy background: `.github/contributing.md` (AI-Assisted Contributions) and `AGENTS.md` section 11.\n\n## 1. Keep it small\n\nA PR does one thing and builds on its own. If your working tree holds more than one logical change, or a reviewer would struggle to follow it, split it into two or more PRs:\n\n- refactor first, behavior change second\n- core logic first, LCD/UI support second\n- one HAL, board family, or pin file group per PR\n- unrelated fixes in their own PRs\n\nTo split: create a new branch from `upstream/bugfix-2.1.x` for each piece and bring over only its changes (`git checkout <work-branch> -- <paths>` or `git cherry-pick -n`, then commit selectively with `git add -p`). Build each branch separately. State any dependency between the PRs in their descriptions.\n\n## 2. Branch\n\n    git fetch upstream\n    git checkout -b my_change upstream/bugfix-2.1.x\n\nPRs target `bugfix-2.1.x`, never a release branch.\n\n## 3. Before committing\n\n- Format only your changed files (skill: marlin-code-style).\n- Build the affected environments (skill: marlin-build-test).\n- Restore the config files after testing (`buildroot/bin/restore_configs` discards uncommitted edits to them, so commit any real config changes first). Don't commit test-only config edits.\n- Add tests for any new feature or option: a `buildroot/tests/<env>/config-NN.ini` build test, and a unit test under `Marlin/tests` when the logic can run without hardware (skill: marlin-build-test).\n- Review `git diff` for unrelated changes.\n\n## 4. Commit\n\n- Imperative mood, 72 characters or fewer on the first line, gitmoji-style emoji prefix like recent `git log` entries (🐛 fix, ✨ feature, ⚡️ performance, 🔧 config, 🩹 minor fix, 🧑‍💻 developer, 🚸 UX).\n- One logical change per commit. Squash fixups.\n\n## 5. Keep current\n\nRebase, don't merge:\n\n    git fetch upstream\n    git rebase upstream/bugfix-2.1.x\n    git push -f\n\n## 6. Open the PR\n\nFill in every section of `.github/pull_request_template.md`. No issue numbers in the title. List what you built and tested, and on what hardware if any. Attach a Configurations ZIP if reviewers need one to test.\n\nThe `mf*` helper scripts (`mfnew`, `mfrb`, `mfpr`, ...) described at https://marlinfw.org/docs/development/git_scripts.html are optional conveniences for these steps.\n","buildroot/skills/marlin-unit-tests/SKILL.md":"---\nname: marlin-unit-tests\ndescription: Use when writing or running Marlin's native unit tests (make unit-test-*), or when a native unit-test build fails locally, especially on macOS.\n---\n\n# Marlin Native Unit Tests\n\nBackground: `test/README.md`. For adding a test to a feature PR, see also the unit-test section of skill marlin-build-test.\n\n## Layout\n\n- Test sources: `Marlin/tests/<area>/test_<name>.cpp` (Unity, `MARLIN_TEST(group, name)`).\n- Test configurations: `test/NNN-name.ini`. Each one builds a separate test binary with a different Marlin configuration. Suites are discovered from this folder by `buildroot/share/PlatformIO/scripts/collect-code-tests.py`.\n- The PlatformIO environment is `linux_native_test` in `ini/native.ini`. It is built on the Linux native HAL, with `-Werror`.\n\n## Run\n\n    make unit-test-all-local                              # all configs\n    make unit-test-single-local UNIT_TEST_CONFIG=default  # one config (test/001-default.ini)\n    make unit-test-all-local-docker                       # same, in a container\n\n## Write one\n\n1. Copy the license header and macros from an existing test, such as `Marlin/tests/feature/test_runout.cpp`.\n2. Wrap the file in `#if ENABLED(FEATURE)` so it compiles only for configs that enable the feature.\n3. If no `test/*.ini` enables the feature, add one with `motherboard = BOARD_SIMULATED` and only the options required to pass the sanity checks.\n4. Test logic, not hardware: parsing, math, conversions, state machines. Anything needing real pins or timers isn't a good candidate.\n\n## macOS\n\nOn macOS `/usr/bin/gcc` is Clang, which cannot build Marlin. Install GCC with MacPorts or Homebrew, then:\n\n    buildroot/bin/mac_gcc macports      # or: homebrew   (apple resets to the system compiler)\n    which gcc                           # should now be the real GCC\n\nIf the native build fails on macOS, first check the compiler diagnostics and which `gcc` you got. Don't assume the failure is a bug in the test wiring.\n\n## Pitfalls\n\n- Test files are compiled with `-Werror`. An unused variable or sign-compare warning fails the run.\n- Don't work around a compiler warning with duplicate includes or duplicate keys in the INI.\n- Run the test configs one at a time. They share the `.pio` build directory.\n","AGENTS.md":"# Marlin 3D Printer Firmware\n\n> This file consolidates the project summary (`docs/project-summary.md`) into the top-level agent reference. It is the canonical orientation document for the whole repo. Per-HAL working notes live in `Marlin/src/HAL/<FAMILY>/AGENTS.md` (e.g. `Marlin/src/HAL/AT32/AGENTS.md`).\n\n## 1. Executive Summary\n\nMarlin is an open-source firmware for 3D printers, written in C/C++ and built on the Arduino framework via PlatformIO. It sits between a host computer (running slicer software) and the printer's hardware, translating G-code movement commands into precise stepper motor control, temperature regulation, and peripheral management. The firmware supports 15+ microcontroller platforms (STM32, AVR, ESP32, RP2040, and more), 400+ pin configuration files across 24 board families, and a modular feature system with 80+ enable/disable features. At over 2,500 source files, Marlin is one of the most widely deployed 3D printer firmware projects in the world, licensed under GPL v3.0.\n\n## 2. Architecture Overview\n\nMarlin follows a layered embedded firmware architecture organized around a Hardware Abstraction Layer (HAL), a G-code command processing pipeline, a set of core motion/thermal modules, and a rich feature system. The firmware's singleton entry point is the `Marlin` class (`MarlinCore`), which manages global state and the main control loop.\n\n```mermaid\ngraph TB\n    subgraph Environment[\"Printer Environment\"]\n        Host[\"Host Computer\\n(Slicer / Host Software)\"]\n        SD[\"SD Card\\n(SdFat)\"]\n        Sensors[\"Thermistors &\\nEndstops\"]\n        Motors[\"Stepper Motors\\n&amp; Drivers\"]\n        Heaters[\"Heaters &amp; Fans\"]\n        Display[\"LCD / TFT / DWIN\"]\n    end\n\n    subgraph Marlin[\"Marlin Firmware\"]\n        subgraph GCode[\"G-Code Subsystem\"]\n            Queue[\"GCodeQueue\\nRing Buffer\"]\n            Parser[\"GCodeParser\\nParameter Extraction\"]\n            Suite[\"GcodeSuite\\nCommand Dispatcher\"]\n        end\n        subgraph Modules[\"Core Modules\"]\n            Motion[\"Motion Planner\"]\n            Planner[\"Block Planner\"]\n            Stepper[\"Stepper Driver\"]\n            Temp[\"Temperature Manager\"]\n        end\n        subgraph Features[\"Feature System\\n80+ modules\"]\n            BedLevel[\"Bed Leveling\"]\n            PowerLoss[\"Power Loss Recovery\"]\n            Runout[\"Filament Runout\"]\n            MMU[\"Multi-Material\\nUnit\"]\n        end\n        subgraph HAL[\"Hardware Abstraction Layer\"]\n            Shared[\"HAL Shared Code\"]\n            Platforms[\"15+ Platform HALs\"]\n        end\n    end\n\n    Host -->|\"USB Serial\\n250k baud\"| Suite\n    SD -->|\"SD Interface\"| Queue\n    Suite --> Parser\n    Parser --> Suite\n    Queue --> Suite\n    Suite --> Motion\n    Suite --> Temp\n    Motion --> Planner\n    Planner --> Stepper\n    Stepper --> Motors\n    Temp --> Heaters\n    Temp --> Sensors\n    Display <-->|\"SPI/I2C/UART\"| Suite\n```\n\nThe firmware communicates with the host via serial at 250,000 baud (configurable), receiving G-code commands line-by-line. Commands are queued in a circular buffer (`GCodeQueue::ring_buffer`), parsed by `GCodeParser`, and dispatched by `GcodeSuite` to category-specific handlers (motion, temperature, configuration, calibration, etc.). The core modules handle the low-level real-time tasks: the planner generates motion blocks with jerk control and acceleration profiling, the stepper driver issues pulse trains, and the temperature manager runs PID loops on thermistor inputs.\n\n### Supported Hardware Platforms\n\n| Platform Family | MCUs | Architecture | Notes |\n| --- | --- | --- | --- |\n| STM32 (various) | STM32F0, F1, F4, F7, H7, G0 | ARM Cortex-M | Primary 32-bit target |\n| AVR | ATmega2560, ATmega1280, AT90USB | 8-bit AVR | Legacy support |\n| ESP32 | ESP32-S3, ESP32-S2 | Xtensa/RISC-V | WiFi/BLE capability |\n| SAMD | SAMD21, SAMD51 | ARM Cortex-M0+/M4 | Adafruit Feather M4 |\n| RP2040 | RP2040, RP2350 | ARM Cortex-M33 | Raspberry Pi Pico |\n| LPC | LPC1768, LPC1769 | ARM Cortex-M3 | RAMBo, Melzi boards |\n| GD32 | GD32F1, GD32F3 | ARM Cortex-M3 | GigaDevice clones |\n| HC32 | HC32F4 | ARM Cortex-M4 | JCEC chips |\n| AT32 | AT32F4 | ARM Cortex-M4 | Artery Tek |\n| Teensy | 3.1/3.2, 3.5/3.6, 4.0/4.1 | ARM Cortex-M4/M7 | PJRC boards |\n| Linux Simulator | x86/x64 | Native | CI/testing |\n| Native Simulator | Any | Native | Unit testing |\n\n## 3. Processing Pipeline\n\nThe G-code processing pipeline follows a well-defined flow from input ingestion through parsing, dispatch, and hardware actuation.\n\n```mermaid\nflowchart TD\n    InputSerial[\"Serial Input\\nUSB / UART\"] --> Queue\n    InputSD[\"SD Card\\nSdFat\"] --> Queue\n    InputProgmem[\"Program Memory\\nPROGMEM\"] --> Queue\n\n    Queue[\"GCodeQueue\\nRing Buffer\\nBUFSIZE entries\"] --> Parser\n\n    Parser[\"GCodeParser\\n• Letter/Code/Subcode\\n• Parameter extraction\\n• FASTER_GCODE_PARSER\\n  pre-scanned flags\"] --> Suite\n\n    Suite[\"GcodeSuite\\nCommand Dispatcher\\n• Subcommand routing\\n• Parameter validation\\n• Endstop events\"]\n\n    Suite --> CatMotion[\"Motion\\nG0/G1/G2/G3\"]\n    Suite --> CatTemp[\"Temperature\\nM104/M105/M106/M107/M109\"]\n    Suite --> CatConfig[\"Configuration\\nM200-M205/M301/M92\"]\n    Suite --> CatCalibrate[\"Calibration\\nG28/G33/G34/G425\"]\n    Suite --> CatSD[\"SD Card\\nM20-M34/M928\"]\n\n    CatMotion --> Planner\n    CatTemp --> Temp\n    CatCalibrate --> Motion\n\n    Planner[\"Block Planner\\n• Trapezoidal profile\\n• Jerk control\\n• Buffer: BLOCK_BUFFER_SIZE\"] --> Stepper\n    Motion[\"Motion\\n• axis_position()\\n• relative_mode\\n• dual_x_carriage\"] --> Planner\n    Temp[\"Temperature\\n• PID loops\\n• thermistor tables\\n• safety checks\"] --> Heaters\n\n    Stepper[\"Stepper Driver\\n• issue_pending()\\n• TMC SPI config\\n• Trinamic drivers\"] --> Output[\"Hardware Outputs\\n• Step/Dir pulses\\n• PWM heat/fan\\n• Serial responses\"]\n```\n\n### Key Pipeline Components\n\n**GCodeQueue** — A circular ring buffer (`RingBuffer`) that holds up to `BUFSIZE` G-code command strings. Commands enter via three injectors: serial input (USB/UART), SD card file reading, and in-firmware injected commands (PROGMEM). The queue's `advance()` method pops the next command and hands it to the parser.\n\n**GCodeParser** — Parses a single G-code line, extracting the command letter (G/M/T), code number, subcode, and all parameter values (X, Y, Z, E, F, S, P, etc.). When `FASTER_GCODE_PARSER` is enabled, the parser pre-scans all parameters into a flags array for O(1) lookups.\n\n**GcodeSuite** — The command dispatcher singleton. Each G/M code maps to a handler method within this class. Commands are routed to category subdirectories under `src/gcode/` (e.g., `motion/`, `temp/`, `config/`, `calibrate/`). The suite also handles pre- and post-command hooks for endstop events, buffer monitoring, and inactivity shutdown.\n\n## 4. Core Components\n\n### 4.1 Firmware Entry Point\n\nThe firmware lifecycle is managed by `MarlinCore.cpp` (~1,777 lines), structured around Arduino's `setup()` and `loop()` functions:\n\n- **`setup()`** — Initializes HAL pins, serial ports, all modules (stepper, temperature, motion, planner, settings), features (runout detection, power loss recovery, LEDs), and the UI subsystem. Each initialization block is wrapped in `SETUP_RUN()` for debug logging in dev mode.\n- **`loop()`** — Runs continuously (infinite loop on AVR, structured loop on 32-bit platforms). Each iteration calls `marlin.idle()`, processes the G-code queue, checks power-off timers, and handles endstop events.\n\n### 4.2 Singleton Architecture\n\nGlobal state is managed through singletons:\n\n| Singleton | File | Responsibility |\n| --- | --- | --- |\n| `marlin` | `MarlinCore.cpp/h` | Global state machine, inactivity management, kill/suicide, heatup waits |\n| `gcode` | `gcode/gcode.cpp` | G-code command dispatch |\n| `queue` | `gcode/queue.h` | Command queue ring buffer |\n| `planner` | `module/planner.cpp` | Motion block buffer and trapezoidal profiling |\n| `stepper` | `module/stepper.cpp` | Step pulse generation and TMC driver config |\n| `thermalManager` | `module/temperature.cpp` | PID loops, thermistor reading, safety monitoring |\n| `card` | `sd/cardreader.cpp` | SD card file system operations |\n| `ui` | `lcd/marlinui.cpp/h` | LCD/UI state machine and menu system |\n\n### 4.3 Module System\n\nThe `module/` directory contains the core motion and thermal management classes:\n\n| Module | File | Description |\n| --- | --- | --- |\n| **Motion** | `motion.h/cpp` | High-level axis positioning, coordinate transforms, joint interpolation |\n| **Planner** | `planner.h/cpp` | Block buffer, trapezoidal velocity profiling, junction deviation |\n| **Stepper** | `stepper.h/cpp` | Step pulse generation, direction control, TMC Trinamic SPI configuration |\n| **Temperature** | `temperature.h/cpp` | PID autotune (M303), thermistor tables, hotend/bed/heater management |\n| **Endstops** | `endstops.h/cpp` | Endstop interrupt handling, homing logic, soft endstops |\n| **Probe** | `probe.h/cpp` | Bed probing (G30, G31/G32, G38, bltouch), Z-offset management |\n| **Settings** | `settings.h/cpp` | EEPROM persistence, configuration storage and retrieval |\n| **PrintCounter** | `printcounter.h/cpp` | Print time, filament usage, and job statistics |\n| **Servo** | `servo.h/cpp` | Servo control for solenoids, lid mechanisms |\n| **Delta/Scara/Polar** | `delta.h`, `scara.h`, `polar.h` | Kinematic transforms for non-Cartesian printers |\n| **Tool Change** | `tool_change.h/cpp` | Multi-extruder tool switching, hotend offset, PTFE purge |\n\n### 4.4 Feature System\n\nThe `feature/` directory contains 80+ optional modules, each gated by a `#if ENABLED(FEATURE_NAME)` preprocessor check:\n\n**Motion Features:** Linear Advance (`linearadvance`), Pressure Advance, Adaptive Multi-Axis Stepping, Direct Stepping, Bresenham acceleration, Resonance Compensation, X-Axis Twist Correction, Z-Stepper Alignment, Backlash Compensation.\n\n**Thermal Features:** Automatic PID Tuning (M303), Thermal Protection, Probe Temperature Compensation, Mixed Extruder (`mixing`), Bowden/Direct Drive Retraction (`fwretract`).\n\n**Peripheral Features:** Power Loss Recovery (`powerloss`), Filament Runout Detection (`runout`), Ethernet Connectivity (`ethernet`), RS485 Communication (`rs485`), MMU/Multi- Material Unit (`mmu`, `mmu3`), Solenoid Control (`solenoid`), Spindle/Laser (`spindle_laser`), Case Lights (`caselight`), LED Color Control (`leds`).\n\n**UI Features:** Touch Screen (`touch`), LVGL TFT UI (`mks_ui`), DWIN Display (`dwin`), Extensible UI (`extui`), Password Protection (`password`), Joystick Input (`joystick`).\n\n**Bed Leveling:** Unified Bed Leveling (UBL), Manual Bed Leveling (MBL), Automatic Bed Leveling (ABL), G26 Mesh Validation, G35 Auto Bed Leveling, Bed Level Visualizer.\n\n## 5. G-Code Command Reference\n\nMarlin implements 100+ G-code commands organized into 14 categories under `src/gcode/`:\n\n| Category | Directory | Key Commands | Description |\n| --- | --- | --- | --- |\n| **Motion** | `motion/` | G0, G1, G2, G3, G5, G6, G80, M400, M290 | Linear/arc moves, rapid moves, dwell, wait, jog |\n| **Temperature** | `temp/` | M104/M105/M106/M107/M109, M140/M190, M303 | Hotend/bed temp set/read, fan control, PID autotune |\n| **Configuration** | `config/` | M200-M205, M301, M92, M43, M218, M217 | Extruder steps, accel/jerk, PID, hotend offset, bowden length |\n| **Calibration** | `calibrate/` | G28, G33, G34, G425, M48, M566, M665 | Home, delta calibration, nozzle purge, backlash, bellows |\n| **Geometry** | `geometry/` | G17-G19, G53-G59, G92, M206, M428 | Plane selection, coordinate systems, position reset, offsets |\n| **Control** | `control/` | M17/M18/M84, M80/M81, M3-M5, M7-M9, T | Enable axes, power, spindle, tool select, feedrate/extruder units |\n| **Probe** | `probe/` | G30, G31/G32, G38, M851, M401/M402, M102 | Bed probe, probe type, probe actions |\n| **SD Card** | `sd/` | M20-M34, M928, M1001, M1003 | File listing, select, start, stop, abort, load, save |\n| **LCD** | `lcd/` | M0, M1, M73, M117, M145, M250, M300 | Pause, progress, message, color, beep |\n| **Host** | `host/` | M16, M110, M113, M114, M115, M118, M119, M154, M360, M876 | Line numbers, machine info, endstop report, kinematic config |\n| **EEPROM** | `eeprom/` | M500, M501, M502, M503, M504 | Save/load/reset/settings EEPROM operations |\n| **Stats** | `stats/` | M31, M75-M78 | Print time, filament used, errors, buffer stats |\n| **Units** | `units/` | G20/G21, M82/M83, M149 | mm/inch, extrusion mode, temp unit display |\n| **OTA** | `ota/` | M936 | Over-the-air firmware update |\n\n## 6. Infrastructure & Deployment\n\n### 6.1 Build System\n\nMarlin uses PlatformIO as its primary build system, configured via `platformio.ini`:\n\n- **Framework**: Arduino (via PlatformIO)\n- **Build flags**: `-g3 -D__MARLIN_FIRMWARE__ -DNDEBUG -fsingle-precision-constant`\n- **Source filter**: Dynamic inclusion/exclusion of files based on target board via pre-build scripts (`configuration.py`, `common-dependencies.py`, `preflight-checks.py`)\n- **Include directory**: `Marlin/src/`\n- **Board definitions**: Custom PlatformIO board definitions in `buildroot/share/PlatformIO/boards/`\n\n### 6.2 Configuration System\n\nConfiguration is managed through a layered preprocessor conditional system:\n\n1. **`Configuration.h`** — User-editable hardware and feature configuration\n2. **`Configuration_adv.h`** — Advanced/experimental feature toggles\n3. **`Version.h`** — Build version, machine name, website URL\n4. **`inc/Conditionals-*.h`** — Auto-generated feature flags derived from Configuration.h\n5. **`inc/MarlinConfig.h`** — Prefix header including all conditionals, types, and sanity checks\n\nThe conditional system ensures that only enabled features compile, minimizing firmware footprint for resource-constrained 8-bit platforms.\n\n### 6.3 CI/CD Pipeline\n\nGitHub Actions workflows run on every pull request and push:\n\n| Workflow | Purpose |\n| --- | --- |\n| `ci-build-tests.yml` | Compiles Marlin for all supported boards (matrix strategy) |\n| `ci-unit-tests.yml` | Runs C++ unit tests on the native simulator |\n| `ci-validate-boards.yml` | Validates board pin configurations |\n| `ci-validate-pins.yml` | Checks for missing/invalid pin definitions |\n| `ci-validate-lines.yml` | Validates line count limits for 8-bit boards |\n| `check-pr.yml` | PR quality checks |\n\n### 6.4 Docker Build Environment\n\nA Dockerfile (`docker/Dockerfile`) provides a reproducible build environment:\n\n```dockerfile\nFROM python:3.11-bookworm\nRUN pip install -U platformio PyYaml\nWORKDIR /code\n```\n\n### 6.5 Testing\n\n- **Unit tests**: Located in `Marlin/tests/`, run via the Linux native simulator HAL\n- **Build tests**: CI compiles against 20+ board targets to catch compile errors\n- **Pin validation**: Automated checks for pin conflicts and missing definitions\n- **SdFat**: Integrated SdFat library for SD card file operations (Sd2Card, SdBaseFile, SdVolume)\n\n## 7. Extension Patterns\n\n### 7.1 Adding a New Feature\n\n1. Create your feature files in `Marlin/src/feature/` (e.g., `my_feature.h` and `my_feature.cpp`)\n2. Add `#define MY_FEATURE` to `Configuration_adv.h`\n3. The conditional system in `inc/Conditionals-*.h` will auto-generate `HAS_MY_FEATURE`\n4. Use `#if ENABLED(MY_FEATURE)` guards in your code\n5. Include your header from `MarlinCore.cpp` under the appropriate `#if ENABLED()` block\n6. Register G-code handlers in `gcode/gcode.cpp` or a new category subdirectory\n\n### 7.2 Adding a New G-Code Command\n\n1. Create a new file in the appropriate `src/gcode/<category>/` directory (e.g., `M1000.cpp`)\n2. Implement the handler method in the `GcodeSuite` class\n3. Register the command in the `process_commands()` method of `GcodeSuite`\n4. The command will be dispatched automatically when the parser encounters the letter/code\n\n### 7.3 Adding Board Support\n\n1. Create a new PlatformIO board definition in `buildroot/share/PlatformIO/boards/`\n2. Add pin definitions in `Marlin/src/pins/<family>/pins_<board>.h`\n3. If the MCU is new, add HAL support in `Marlin/src/HAL/<MCU_FAMILY>/`\n4. Update `ini/<platform>.ini` in `platformio.ini`'s `extra_configs`\n5. Run `ci-validate-pins.yml` to verify pin assignments\n\n### 7.4 Adding a Display Backend\n\n1. Create backend files in `Marlin/src/lcd/<backend>/`\n2. Implement the required display interface (SPI/I2C/UART)\n3. Add `#define ULTIPANEL` or display-specific `#define` to `Configuration.h`\n4. The LCD subsystem routes through `MarlinUI` to the appropriate backend\n\n## 8. Rules & Anti-Patterns\n\n### Best Practices\n\n- **Conditional compilation**: Always gate platform-specific code with `#if ENABLED()` or `#ifdef ARDUINO_ARCH_XXX`\n- **PROGMEM usage**: Store large strings and lookup tables in program memory on 8-bit platforms\n- **Interrupt safety**: Use `WAIT`/`NOOP` patterns for stepper ISR synchronization\n- **EEPROM limits**: Respect memory constraints — 8-bit boards have limited EEPROM (4KB-8KB)\n- **Thread safety**: The main loop and stepper ISR share state — use volatile and careful locking\n- **Static analysis**: Use `bug_on()` macros for compile-time assertion checks\n- **Code size**: 8-bit targets (AVR) have strict flash limits — enable only necessary features\n\n### Anti-Patterns\n\n- **Don't add features to `Configuration.h`** — use `Configuration_adv.h` for advanced/experimental features\n- **Don't modify HAL shared code** without understanding platform implications\n- **Don't use floating-point on AVR** without explicit `float` typing — the compiler defaults to single precision\n- **Don't block in the main loop** — all long operations must yield to `idle()` or use non-blocking patterns\n- **Don't assume 32-bit semantics** — code must compile for both 8-bit AVR and 32-bit ARM\n- **Don't hardcode pin numbers** — always use the pin definition headers\n\n## 9. Dependencies\n\n### Build Tools\n\n| Dependency       | Version           | Purpose                    |\n| ---------------- | ----------------- | -------------------------- |\n| PlatformIO       | Latest dev        | Build system and framework |\n| Arduino Core     | Platform-specific | Microcontroller SDK        |\n| GCC ARM Embedded | 12.x              | ARM cross-compiler         |\n| avr-gcc          | 7.x               | AVR cross-compiler         |\n\n### Libraries (integrated)\n\n| Library    | Purpose                                             |\n| ---------- | --------------------------------------------------- |\n| SdFat      | SD card file system (Sd2Card, SdBaseFile, SdVolume) |\n| u8glib     | OLED/LCD display driver (embedded in HAL)           |\n| LVGL       | TFT graphical UI (optional, for MKS UI)             |\n| heatshrink | G-code decompression for SD prints                  |\n\n### Platform Libraries\n\n| Platform | Libraries                     |\n| -------- | ----------------------------- |\n| STM32    | STM32 HAL/LL, STM32duino core |\n| AVR      | Arduino AVR core, TimerOne    |\n| ESP32    | ESP-IDF components, WiFi      |\n| RP2040   | Arduino RP2040 core, PIOasm   |\n| Teensy   | Teensyduino core, Bounce2     |\n| SAMD     | Arduino SAMD core             |\n\n## 10. Code Structure\n\n```\nMarlinFirmware/                  # Repo root (this directory). `cd` here for PlatformIO builds.\n├── Marlin/                      # The Arduino \"sketch\" (application source + config files)\n│   ├── Configuration.h          # User hardware configuration\n│   ├── Configuration_adv.h      # Advanced/experimental toggles\n│   ├── Marlin.ino               # Board-specific entry stub\n│   ├── Makefile                 # Alternative build (simulator)\n│   ├── Version.h                # Build version, machine name\n│   ├── config.ini               # PlatformIO extra config\n│   ├── lib/                     # Integrated libraries\n│   └── src/                     # Application source (see below)\n├── buildroot/                   # Build infrastructure\n├── ini/                         # PlatformIO platform configs\n├── docs/                        # Project documentation\n├── docker/                      # Docker build environment\n├── .github/workflows/           # CI/CD pipelines\n└── platformio.ini               # Main PlatformIO configuration\n```\n\n### Marlin/src layout\n\n```\nMarlin/src/\n├── MarlinCore.cpp/h         # Firmware entry, setup(), loop(), Marlin singleton\n├── core/                    # Core utilities (serial, language, types, mstring)\n│   ├── gcode/                   # G-code subsystem\n│   │   ├── gcode.cpp/h          # GcodeSuite dispatcher\n│   │   ├── parser.cpp/h         # GCodeParser\n│   │   ├── queue.cpp/h          # GCodeQueue ring buffer\n│   │   ├── motion/              # G0-G6, M290, M400\n│   │   ├── temp/                # M104-M109, M140-M193, M303\n│   │   ├── config/              # M200-M205, M301, M92, M218\n│   │   ├── calibrate/           # G28, G33, G34, G425, M48\n│   │   ├── bedlevel/            # G26, G35, G42, M420\n│   │   ├── geometry/            # G17-G19, G53-G59, G92\n│   │   ├── probe/               # G30-G38, M851, M401\n│   │   ├── sd/                  # M20-M34, M928\n│   │   ├── lcd/                 # M0, M1, M73, M117\n│   │   ├── host/                # M16, M110-M119\n│   │   ├── eeprom/              # M500-M504\n│   │   ├── stats/               # M31, M75-M78\n│   │   ├── units/               # G20/G21, M82/M83\n│   │   └── control/             # M3-M5, M7-M9, M17-M85, T\n│   ├── module/                  # Core modules\n│   │   ├── motion.h/cpp         # Axis positioning, kinematics\n│   │   ├── planner.h/cpp        # Block buffer, trapezoidal profiling\n│   │   ├── stepper.h/cpp        # Step pulses, TMC drivers\n│   │   ├── temperature.h/cpp    # PID, thermistors, safety\n│   │   ├── endstops.h/cpp       # Endstop interrupts, homing\n│   │   ├── probe.h/cpp          # Bed probing, Z-offset\n│   │   ├── settings.h/cpp       # EEPROM persistence\n│   │   ├── delta.h/cpp          # Delta kinematics\n│   │   ├── scara.h/cpp          # Scara kinematics\n│   │   ├── polar.h/cpp          # Polar kinematics\n│   │   ├── polargraph.h/cpp     # Polar graph kinematics\n│   │   ├── servo.h/cpp          # Servo control\n│   │   ├── printcounter.h/cpp   # Print statistics\n│   │   ├── tool_change.h/cpp    # Multi-extruder switching\n│   │   ├── ft_motion/           # Fine-tune motion\n│   │   └── stepper/             # Stepper internals, Trinamic\n│   ├── feature/                 # 80+ optional features\n│   │   ├── bedlevel/            # UBL, MBL, ABL\n│   │   ├── leds/                # LED color control\n│   │   ├── mmu/                 # Multi-material unit\n│   │   ├── mmu3/                # MMU3 variant\n│   │   ├── powerloss.h/cpp      # Power loss recovery\n│   │   ├── runout.h/cpp         # Filament runout detection\n│   │   ├── pause.h/cpp          # Pause/resume\n│   │   ├── mixing.h/cpp         # Mixed extruder\n│   │   ├── fwretract.h/cpp      # Firmware retraction\n│   │   ├── resonance/           # Resonance compensation\n│   │   ├── spindle_laser.h/cpp  # Spindle/laser control\n│   │   ├── ethernet.h/cpp       # Ethernet connectivity\n│   │   ├── solenoid.h/cpp       # Solenoid control\n│   │   ├── password/            # Password protection\n│   │   ├── joystick.h/cpp       # Joystick input\n│   │   ├── tmc_util.h/cpp       # TMC driver utilities\n│   │   └── ...                  # 60+ more features\n│   ├── lcd/                     # Display/UI subsystem\n│   │   ├── marlinui.h/cpp        # Main UI controller\n│   │   ├── HD44780/             # Hitachi HD44780 LCD\n│   │   ├── dogm/                # DOGM OLED/LCD\n│   │   ├── tft/                 # TFT displays\n│   │   ├── tft_io/              # TFT I/O drivers\n│   │   ├── dwin/                # DWIN displays\n│   │   ├── extui/               # Extensible UI (MKS LVGL)\n│   │   ├── menu/                # Menu system\n│   │   ├── touch/               # Touch screen\n│   │   ├── language/            # Multi-language strings\n│   │   └── sovol_rts/           # Sovol RTS display\n│   ├── libs/                    # Utility libraries\n│   │   ├── bresenham.h          # Bresenham line algorithm\n│   │   ├── vector_3.h/cpp       # 3D vector math\n│   │   ├── circularqueue.h      # Generic circular queue\n│   │   ├── adc/                 # ADC utilities\n│   │   ├── heatshrink/          # Decompression\n│   │   ├── nozzle.cpp/h         # Nozzle cleaning\n│   │   ├── numtostr.h/cpp       # Number-to-string\n│   │   ├── hex_print.h/cpp      # Hex printing\n│   │   ├── crc16.h/cpp          # CRC-16 calculation\n│   │   ├── stopwatch.h/cpp      # Stopwatch utility\n│   │   ├── least_squares_fit.h  # Least squares fitting\n│   │   └── ...                  # 15+ utility libs\n│   ├── sd/                      # SD card subsystem\n│   │   ├── cardreader.h/cpp     # Card reader interface\n│   │   ├── SdFat*               # SdFat library (file system)\n│   │   ├── disk_io_driver.h     # Disk I/O abstraction\n│   │   └── usb_flashdrive/      # USB flash drive support\n│   ├── pins/                    # Pin definitions (400+ files)\n│   │   ├── pins.h               # Pin inclusion router\n│   │   ├── ramps/               # Ramps board family\n│   │   ├── mega/                # Mega board family\n│   │   ├── rambo/               # Rambo board family\n│   │   ├── sanguino/            # Sanguino board family\n│   │   ├── stm32f1/             # STM32F1 boards\n│   │   ├── stm32f4/             # STM32F4 boards\n│   │   ├── stm32f7/             # STM32F7 boards\n│   │   ├── stm32h7/             # STM32H7 boards\n│   │   ├── stm32g0/             # STM32G0 boards\n│   │   ├── stm32f0/             # STM32F0 boards\n│   │   ├── sam/                 # SAM boards\n│   │   ├── samd/                # SAMD boards\n│   │   ├── rp2040/              # RP2040 boards\n│   │   ├── lpc1768/             # LPC1768 boards\n│   │   ├── lpc1769/             # LPC1769 boards\n│   │   ├── teensy2/             # Teensy 2.x boards\n│   │   ├── teensy3/             # Teensy 3.x boards\n│   │   ├── teensy4/             # Teensy 4.x boards\n│   │   ├── esp32/               # ESP32 boards\n│   │   ├── gd32f1/              # GD32F1 boards\n│   │   ├── gd32f3/              # GD32F3 boards\n│   │   ├── hc32f4/              # HC32F4 boards\n│   │   ├── at32f4/              # AT32F4 boards\n│   │   └── native/              # Native/simulator\n│   ├── HAL/                     # Hardware Abstraction Layer\n│   │   ├── HAL.h                # HAL interface definition\n│   │   ├── platforms.h          # Platform selection\n│   │   ├── shared/              # Cross-platform HAL code\n│   │   │   ├── HAL.cpp/h        # Shared HAL implementation\n│   │   │   ├── Delay.h          # Safe delay function\n│   │   │   ├── eeprom_api.h     # EEPROM API\n│   │   │   ├── cpu_exception/   # Exception handling\n│   │   │   └── backtrace/       # Stack backtrace\n│   │   ├── STM32/               # STM32 platform HAL\n│   │   ├── AVR/                 # AVR platform HAL\n│   │   ├── ESP32/               # ESP32 platform HAL\n│   │   ├── DUE/                 # Arduino Due HAL\n│   │   ├── SAMD21/              # SAMD21 HAL\n│   │   ├── SAMD51/              # SAMD51 HAL\n│   │   ├── RP2040/              # RP2040 HAL\n│   │   ├── LPC1768/             # LPC1768 HAL\n│   │   ├── GD32_MFL/            # GD32 HAL\n│   │   ├── HC32/                # HC32 HAL\n│   │   ├── AT32/                # AT32 HAL\n│   │   ├── TEENSY31_32/         # Teensy 3.1/3.2 HAL\n│   │   ├── TEENSY35_36/         # Teensy 3.5/3.6 HAL\n│   │   ├── TEENSY40_41/         # Teensy 4.0/4.1 HAL\n│   │   ├── STM32F1/             # STM32F1 HAL\n│   │   ├── LINUX/               # Linux simulator HAL\n│   │   └── NATIVE_SIM/          # Native simulator HAL\n│   └── tests/                   # Unit tests\n│       ├── unit_tests.h/cpp     # Test suite\n│       └── *.ini                # Test configurations\n├── docs/                        # Project documentation\n│   ├── AGENTS.md                # This document\n│   └── diagrams/                # Architecture diagrams\n│       ├── high-level-architecture.drawio\n│       ├── processing-pipeline.drawio\n│       └── component-relationships.drawio\n├── buildroot/                   # Build infrastructure\n│   ├── share/PlatformIO/        # PlatformIO board definitions & scripts\n│   ├── test-gcode/              # G-code test files\n│   └── tests/                   # Build test configurations\n├── ini/                         # PlatformIO platform configs\n│   ├── avr.ini, due.ini, esp32.ini\n│   ├── stm32-common.ini, stm32f1.ini, stm32f4.ini\n│   ├── stm32f7.ini, stm32h7.ini, stm32g0.ini\n│   ├── samd21.ini, samd51.ini\n│   ├── raspberrypi.ini, teensy.ini\n│   ├── at32.ini, gd32.ini, hc32.ini\n│   ├── lpc176x.ini, native.ini\n│   ├── features.ini, renamed.ini\n│   └── stm32f1-maple.ini\n├── test/                        # Root-level test configurations\n├── docker/                      # Docker build environment\n├── .github/workflows/           # CI/CD pipelines\n└── platformio.ini               # Main PlatformIO configuration\n```\n\n## 11. Constructing a Pull Request\n\nMarlin accepts AI-assisted code (see `.github/contributing.md`). It must be sensible and reviewable. Follow these rules when preparing a PR.\n\n### Scope and size\n\n- **One PR, one purpose.** A PR should be a single self-contained change that builds and passes CI on its own.\n- **Split large changes.** If a change touches several unrelated areas, or is large enough that a reviewer can't hold it in their head, split it into two (or more) smaller PRs. Typical splits: refactor first, feature second; core logic first, UI/LCD support second; one HAL or board family per PR; sanity checks and docs with the feature they cover.\n- **Order dependent PRs.** If PR B needs PR A, say so in B's description and don't open B until A is far enough along to be reviewed.\n- **No drive-by changes.** Don't reformat, rename, or tidy code you aren't changing. Keep the diff minimal.\n- **Don't change defaults** in `Configuration.h` / `Configuration_adv.h` unless that is the purpose of the PR. New features are disabled by default.\n\n### Before you start\n\n- Branch from the latest `bugfix-2.1.x` (the working branch). Never target release branches.\n- We occasionally backport fixes to lts-* branches. Very small non-duplicate patches are accepted.\n- Read the neighboring code and follow its conventions. Check `buildroot/skills/` for step-by-step procedures.\n- Read the HAL-specific `AGENTS.md` (`Marlin/src/HAL/<FAMILY>/AGENTS.md`) when touching a HAL.\n\n### Making the change\n\n- Code must compile on 8-bit AVR and 32-bit targets. Mind flash and RAM on AVR. Keep the base build requirements from growing.\n- Gate optional code with `ENABLED()` / `DISABLED()` / `ANY()` / `NONE()` / etc. and add validation to `Marlin/src/inc/SanityCheck.h`.\n- Use a suitable insertion point for Configuration and Conditionals. They follow a strict order set by `Marlin/src/inc/MarlinConfig.h`.\n- Don't hardcode pins; use the pin headers in `src/pins`.\n- When adding or changing a user-facing option, update its comments in `Configuration*.h` and note any `Conditionals`, `Warnings.cpp`, or `config.ini` impacts.\n- Follow the Marlin coding standards (2-space indent, no tabs, spacing as in nearby code). Indent new code similar to surroundings (including preprocessor nesting) and avoid auto-formatters.\n\n### Verifying\n\n- Use `MarlinSimUI` for direct testing in a simulated environment with a rich MCP interface. (And contribute your enhancements to `MarlinSimUI`.)\n- Build at least one affected environment (`buildroot/bin/mftest`; see the `marlin-build-test` skill).\n- **Add tests with new features and options.** Add or update a `buildroot/tests/<env>/config-NN.ini` build test so CI exercises the feature, along with its dependent options.\n- Add a unit test in `Marlin/tests/<area>/` (run with `make unit-test-all-local`) when the logic can be checked without hardware.\n  - Bug fixes should get a test when one can catch the regression. See the `marlin-build-test` skill.\n- Report honestly what was and wasn't tested. Never claim a build or test passed unless you ran it.\n\n### Commit messages and PR description\n\n- Imperative mood, first line of 72 characters or fewer, prefixed with a gitmoji-style emoji as in `git log` (e.g. 🐛 fix, ✨ feature, ⚡️ performance, 🔧 config, 🩹 minor fix, 🧑‍💻 developer tooling, 🚸 UX).\n- Don't put issue numbers in the PR title. Reference them in the description.\n- Fill in every section of `.github/pull_request_template.md`: Description, Requirements, Benefits, Configurations, Related Issues.\n- Keep history clean: rebase on `bugfix-2.1.x` rather than merging, and squash fixups into the commits they belong to.\n\n### Skills\n\nReusable procedures for contributors and their tools live in `buildroot/skills/`. Each skill is a folder with a `SKILL.md`. See `buildroot/skills/README.md`.\n","buildroot/skills/README.md":"# Marlin Contributor Skills\n\nStep-by-step procedures for working on Marlin, written for AI coding agents and equally useful as human cheat sheets. Each skill is a folder containing a `SKILL.md` with YAML front matter (`name`, `description`) followed by instructions. They refer to the scripts in `buildroot/bin` and the `Makefile` so the procedures stay accurate as the scripts evolve.\n\nAll commands assume the current directory is the repository root.\n\n| Skill | Purpose |\n| --- | --- |\n| [marlin-build-test](marlin-build-test/SKILL.md) | Build a board environment, apply test configs, run CI-style tests |\n| [marlin-config-options](marlin-config-options/SKILL.md) | Find, enable, disable, and set configuration options from the command line |\n| [marlin-code-style](marlin-code-style/SKILL.md) | Follow the Marlin coding standards and indentation; leave existing code unformatted |\n| [marlin-pr-workflow](marlin-pr-workflow/SKILL.md) | Prepare a focused, reviewable Pull Request |\n| [marlin-solving-bugs](marlin-solving-bugs/SKILL.md) | Investigate, isolate, fix, and verify a bug report |\n| [marlin-extui-backends](marlin-extui-backends/SKILL.md) | Add or change ExtUI display backends and the ExtUI API |\n| [marlin-unit-tests](marlin-unit-tests/SKILL.md) | Write and run native unit tests; macOS GCC setup |\n| [marlin-codebase-navigation](marlin-codebase-navigation/SKILL.md) | Find definitions, uses, and flows in the source with git and grep |\n| [marlin-hal-work](marlin-hal-work/SKILL.md) | Add a board, extend a HAL, or create a new HAL |\n| [marlin-fork-harvesting](marlin-fork-harvesting/SKILL.md) | Evaluate a Marlin fork and port its features cleanly |\n\n## Adding a skill\n\n- Make a folder `buildroot/skills/<skill-name>/` with a `SKILL.md`.\n- Start `description` with when to use the skill. Keep the skill short and procedural.\n- Refer to scripts in `buildroot/bin` instead of duplicating their logic, and verify every command you document.\n- Skills are for all contributors. Don't include personal paths or maintainer-only procedures.\n- Add the skill to the table above.\n","buildroot/skills/marlin-solving-bugs/SKILL.md":"---\nname: marlin-solving-bugs\ndescription: Use when investigating and fixing a Marlin bug report, from reproducing it to a verified patch. Covers triage, finding the code path, isolating the cause, a minimal root-cause fix, and verification.\n---\n\n# Fix a Marlin Bug\n\nFind the root cause before you write any fix. Marlin has many HALs and a huge number of configuration combinations, and there is no hardware-in-the-loop CI, so the evidence you gather matters more than the size of the patch.\n\n## 1. Triage the report\n\n- Get the exact Marlin version or commit, the board, and `Configuration.h` / `Configuration_adv.h` (a `marlin_config.json` or a Configurations ZIP is best).\n- Find the first version that failed, and the last that worked. If the reporter can test, ask for a bisect (`git bisect`) between those points.\n- Check whether `bugfix-2.1.x` still has the bug. Fixes land there first, and old reports are often already fixed.\n- Files move between releases. Search for the current location of any path in an old report: `ultralcd.*` became `marlinui.*`, and `Conditionals_post.h` became `Conditionals-5-post.h`.\n\n## 2. Find the code path\n\n    git grep -n \"OPTION_NAME\" -- Marlin/src            # who uses an option\n    git log --oneline -20 -- Marlin/src/module/motion.cpp\n    git log -S\"suspicious_string\" --oneline -- Marlin/src\n    git log --oneline 2.1.2..2.1.3 -- Marlin/src/module/probe.cpp\n\n- G-code handlers are in `Marlin/src/gcode/`, with one file per command or small group.\n- Trace which `#if` paths the reporter's config enables. A bug often appears only for one combination of options.\n- A regression usually has a specific commit. Read its diff and its PR discussion to learn what it was meant to do.\n\n## 3. Isolate the cause\n\n- Reproduce on the smallest config you can, using `buildroot/bin/opt_*` to set only the needed options (skill: marlin-config-options).\n- Test as much as possible in the native simulator ([MarlinSimUI](https://github.com/MarlinFirmware/MarlinSimUI)) or a unit test, so you can run and re-run the scenario.\n- Change one thing at a time and keep notes. A temporary `SERIAL_ECHOLNPGM` can help, but remove every debug statement before committing.\n- State your hypothesis and the evidence before coding the fix. If you can, show that reverting the suspect change removes the symptom.\n\n## 4. Fix the root cause\n\n- Fix the wrong logic itself. Don't add workarounds elsewhere that hide the symptom.\n- Make the smallest diff that does it. No reformatting, no unrelated cleanup (skill: marlin-code-style).\n- Think about every configuration the changed code is compiled in. Build with the relevant options both on and off.\n\nCommon causes:\n\n| Symptom | Look for |\n| --- | --- |\n| Wrong distance or speed | mm vs steps vs mm/s units; missing conversion |\n| Works with one option, breaks with another | A missing `ENABLED()` guard, or the wrong layer in `Conditionals-*.h` |\n| Wrong axis | `X_AXIS` used where `Z_AXIS` was meant; macro for the wrong axis |\n| Fails only on AVR | Integer overflow (use `uint32_t`), `double`, RAM and flash limits |\n| Fails only on one board | HAL differences: timers, serial buffering, pin multiplexing |\n| Position drift after an event | A reset of `current_position` or the planner position without `sync_plan_position()` |\n| Lost settings after upgrade | A `settings.cpp` layout change without a version bump |\n\n## 5. Verify\n\n- Build with `mftest` (skill: marlin-build-test). At least one AVR (tight flash) and one 32-bit environment for common code.\n- Run the reporter's scenario again, not just a build. A passing build is not proof the bug is fixed.\n- If the logic is testable without hardware, add a unit test that fails before your fix and passes after.\n- If you add a config interaction, add a check to `Marlin/src/inc/SanityCheck.h`.\n\n## 6. Submit\n\nFollow skill marlin-pr-workflow. In the PR description, say what the root cause was, how you reproduced it, and what you tested (including on what hardware, if any). Reference the issue in the description, not the title. Don't claim the issue is closed until the reporter or another tester confirms it on hardware.\n\n## Pitfalls\n\n- **Debug code left in.** `#define DEBUG_OUT 1`, `DEBUG_ECHOLNPGM`, and temporary prints must not be in the final diff. Check with `git diff | grep -n DEBUG_`.\n- **Stale `case` labels.** After an enum value is removed, a `case REMOVED_VALUE:` can survive a rebase. Grep for removed names when a PR touches enums.\n- **Shared string buffers.** The `numtostr` family (`ftostr*`, `i16tostr*`) writes into one shared buffer. Never pass one function's result into another, and don't hold a pointer across calls.\n- **Variables declared outside the guard of their only uses** cause `-Wunused-variable`, and CI treats warnings as errors. Put the declaration inside the same guard.\n- **A function whose signature uses a type from a guarded header** needs its definition guarded the same way as its declaration.\n- **Verify a premise before relying on it.** For example, `IS_SCARA` also covers TPARA, so grep the macro definition before saying a path doesn't apply to a variant.\n\n## Reading a failed CI run\n\n    gh run view <run_id> --repo MarlinFirmware/Marlin --json jobs -q '.jobs[] | select(.conclusion==\"failure\") | [.databaseId, .name] | @tsv'\n    gh run view <run_id> --job <job_id> --log | grep -E \"error:|FAILED|undefined\" | head -30\n\nA job that failed in under 30 seconds is nearly always a compile or sanity-check error. Longer ones are often timeouts.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Marlin 3D Printer Firmware\n\n> This file consolidates the project summary (`docs/project-summary.md`) into the top-level agent reference. It is the canonical orientation document for the whole repo. Per-HAL working notes live in `Marlin/src/HAL/<FAMILY>/AGENTS.md` (e.g. `Marlin/src/HAL/AT32/AGENTS.md`).\n\n## 1. Executive Summary\n\nMarlin is an open-source firmware for 3D printers, written in C/C++ and built on the Arduino framework via PlatformIO. It sits between a host computer (running slicer software) and the printer's hardware, translating G-code movement commands into precise stepper motor control, temperature regulation, and peripheral management. The firmware supports 15+ microcontroller platforms (STM32, AVR, ESP32, RP2040, and more), 400+ pin configuration files across 24 board families, and a modular feature system with 80+ enable/disable features. At over 2,500 source files, Marlin is one of the most widely deployed 3D printer firmware projects in the world, licensed under GPL v3.0.\n\n## 2. Architecture Overview\n\nMarlin follows a layered embedded firmware architecture organized around a Hardware Abstraction Layer (HAL), a G-code command processing pipeline, a set of core motion/thermal modules, and a rich feature system. The firmware's singleton entry point is the `Marlin` class (`MarlinCore`), which manages global state and the main control loop.\n\n```mermaid\ngraph TB\n    subgraph Environment[\"Printer Environment\"]\n        Host[\"Host Computer\\n(Slicer / Host Software)\"]\n        SD[\"SD Card\\n(SdFat)\"]\n        Sensors[\"Thermistors &\\nEndstops\"]\n        Motors[\"Stepper Motors\\n&amp; Drivers\"]\n        Heaters[\"Heaters &amp; Fans\"]\n        Display[\"LCD / TFT / DWIN\"]\n    end\n\n    subgraph Marlin[\"Marlin Firmware\"]\n        subgraph GCode[\"G-Code Subsystem\"]\n            Queue[\"GCodeQueue\\nRing Buffer\"]\n            Parser[\"GCodeParser\\nParameter Extraction\"]\n            Suite[\"GcodeSuite\\nCommand Dispatcher\"]\n        end\n        subgraph Modules[\"Core Modules\"]\n            Motion[\"Motion Planner\"]\n            Planner[\"Block Planner\"]\n            Stepper[\"Stepper Driver\"]\n            Temp[\"Temperature Manager\"]\n        end\n        subgraph Features[\"Feature System\\n80+ modules\"]\n            BedLevel[\"Bed Leveling\"]\n            PowerLoss[\"Power Loss Recovery\"]\n            Runout[\"Filament Runout\"]\n            MMU[\"Multi-Material\\nUnit\"]\n        end\n        subgraph HAL[\"Hardware Abstraction Layer\"]\n            Shared[\"HAL Shared Code\"]\n            Platforms[\"15+ Platform HALs\"]\n        end\n    end\n\n    Host -->|\"USB Serial\\n250k baud\"| Suite\n    SD -->|\"SD Interface\"| Queue\n    Suite --> Parser\n    Parser --> Suite\n    Queue --> Suite\n    Suite --> Motion\n    Suite --> Temp\n    Motion --> Planner\n    Planner --> Stepper\n    Stepper --> Motors\n    Temp --> Heaters\n    Temp --> Sensors\n    Display <-->|\"SPI/I2C/UART\"| Suite\n```\n\nThe firmware communicates with the host via serial at 250,000 baud (configurable), receiving G-code commands line-by-line. Commands are queued in a circular buffer (`GCodeQueue::ring_buffer`), parsed by `GCodeParser`, and dispatched by `GcodeSuite` to category-specific handlers (motion, temperature, configuration, calibration, etc.). The core modules handle the low-level real-time tasks: the planner generates motion blocks with jerk control and acceleration profiling, the stepper driver issues pulse trains, and the temperature manager runs PID loops on thermistor inputs.\n\n### Supported Hardware Platforms\n\n| Platform Family | MCUs | Architecture | Notes |\n| --- | --- | --- | --- |\n| STM32 (various) | STM32F0, F1, F4, F7, H7, G0 | ARM Cortex-M | Primary 32-bit target |\n| AVR | ATmega2560, ATmega1280, AT90USB | 8-bit AVR | Legacy support |\n| ESP32 | ESP32-S3, ESP32-S2 | Xtensa/RISC-V | WiFi/BLE capability |\n| SAMD | SAMD21, SAMD51 | ARM Cortex-M0+/M4 | Adafruit Feather M4 |\n| RP2040 | RP2040, RP2350 | ARM Cortex-M33 | Raspberry Pi Pico |\n| LPC | LPC1768, LPC1769 | ARM Cortex-M3 | RAMBo, Melzi boards |\n| GD32 | GD32F1, GD32F3 | ARM Cortex-M3 | GigaDevice clones |\n| HC32 | HC32F4 | ARM Cortex-M4 | JCEC chips |\n| AT32 | AT32F4 | ARM Cortex-M4 | Artery Tek |\n| Teensy | 3.1/3.2, 3.5/3.6, 4.0/4.1 | ARM Cortex-M4/M7 | PJRC boards |\n| Linux Simulator | x86/x64 | Native | CI/testing |\n| Native Simulator | Any | Native | Unit testing |\n\n## 3. Processing Pipeline\n\nThe G-code processing pipeline follows a well-defined flow from input ingestion through parsing, dispatch, and hardware actuation.\n\n```mermaid\nflowchart TD\n    InputSerial[\"Serial Input\\nUSB / UART\"] --> Queue\n    InputSD[\"SD Card\\nSdFat\"] --> Queue\n    InputProgmem[\"Program Memory\\nPROGMEM\"] --> Queue\n\n    Queue[\"GCodeQueue\\nRing Buffer\\nBUFSIZE entries\"] --> Parser\n\n    Parser[\"GCodeParser\\n• Letter/Code/Subcode\\n• Parameter extraction\\n• FASTER_GCODE_PARSER\\n  pre-scanned flags\"] --> Suite\n\n    Suite[\"GcodeSuite\\nCommand Dispatcher\\n• Subcommand routing\\n• Parameter validation\\n• Endstop events\"]\n\n    Suite --> CatMotion[\"Motion\\nG0/G1/G2/G3\"]\n    Suite --> CatTemp[\"Temperature\\nM104/M105/M106/M107/M109\"]\n    Suite --> CatConfig[\"Configuration\\nM200-M205/M301/M92\"]\n    Suite --> CatCalibrate[\"Calibration\\nG28/G33/G34/G425\"]\n    Suite --> CatSD[\"SD Card\\nM20-M34/M928\"]\n\n    CatMotion --> Planner\n    CatTemp --> Temp\n    CatCalibrate --> Motion\n\n    Planner[\"Block Planner\\n• Trapezoidal profile\\n• Jerk control\\n• Buffer: BLOCK_BUFFER_SIZE\"] --> Stepper\n    Motion[\"Motion\\n• axis_position()\\n• relative_mode\\n• dual_x_carriage\"] --> Planner\n    Temp[\"Temperature\\n• PID loops\\n• thermistor tables\\n• safety checks\"] --> Heaters\n\n    Stepper[\"Stepper Driver\\n• issue_pending()\\n• TMC SPI config\\n• Trinamic drivers\"] --> Output[\"Hardware Outputs\\n• Step/Dir pulses\\n• PWM heat/fan\\n• Serial responses\"]\n```\n\n### Key Pipeline Components\n\n**GCodeQueue** — A circular ring buffer (`RingBuffer`) that holds up to `BUFSIZE` G-code command strings. Commands enter via three injectors: serial input (USB/UART), SD card file reading, and in-firmware injected commands (PROGMEM). The queue's `advance()` method pops the next command and hands it to the parser.\n\n**GCodeParser** — Parses a single G-code line, extracting the command letter (G/M/T), code number, subcode, and all parameter values (X, Y, Z, E, F, S, P, etc.). When `FASTER_GCODE_PARSER` is enabled, the parser pre-scans all parameters into a flags array for O(1) lookups.\n\n**GcodeSuite** — The command dispatcher singleton. Each G/M code maps to a handler method within this class. Commands are routed to category subdirectories under `src/gcode/` (e.g., `motion/`, `temp/`, `config/`, `calibrate/`). The suite also handles pre- and post-command hooks for endstop events, buffer monitoring, and inactivity shutdown.\n\n## 4. Core Components\n\n### 4.1 Firmware Entry Point\n\nThe firmware lifecycle is managed by `MarlinCore.cpp` (~1,777 lines), structured around Arduino's `setup()` and `loop()` functions:\n\n- **`setup()`** — Initializes HAL pins, serial ports, all modules (stepper, temperature, motion, planner, settings), features (runout detection, power loss recovery, LEDs), and the UI subsystem. Each initialization block is wrapped in `SETUP_RUN()` for debug logging in dev mode.\n- **`loop()`** — Runs continuously (infinite loop on AVR, structured loop on 32-bit platforms). Each iteration calls `marlin.idle()`, processes the G-code queue, checks power-off timers, and handles endstop events.\n\n### 4.2 Singleton Architecture\n\nGlobal state is managed through singletons:\n\n| Singleton | File | Responsibility |\n| --- | --- | --- |\n| `marlin` | `MarlinCore.cpp/h` | Global state machine, inactivity management, kill/suicide, heatup waits |\n| `gcode` | `gcode/gcode.cpp` | G-code command dispatch |\n| `queue` | `gcode/queue.h` | Command queue ring buffer |\n| `planner` | `module/planner.cpp` | Motion block buffer and trapezoidal profiling |\n| `stepper` | `module/stepper.cpp` | Step pulse generation and TMC driver config |\n| `thermalManager` | `module/temperature.cpp` | PID loops, thermistor reading, safety monitoring |\n| `card` | `sd/cardreader.cpp` | SD card file system operations |\n| `ui` | `lcd/marlinui.cpp/h` | LCD/UI state machine and menu system |\n\n### 4.3 Module System\n\nThe `module/` directory contains the core motion and thermal management classes:\n\n| Module | File | Description |\n| --- | --- | --- |\n| **Motion** | `motion.h/cpp` | High-level axis positioning, coordinate transforms, joint interpolation |\n| **Planner** | `planner.h/cpp` | Block buffer, trapezoidal velocity profiling, junction deviation |\n| **Stepper** | `stepper.h/cpp` | Step pulse generation, direction control, TMC Trinamic SPI configuration |\n| **Temperature** | `temperature.h/cpp` | PID autotune (M303), thermistor tables, hotend/bed/heater management |\n| **Endstops** | `endstops.h/cpp` | Endstop interrupt handling, homing logic, soft endstops |\n| **Probe** | `probe.h/cpp` | Bed probing (G30, G31/G32, G38, bltouch), Z-offset management |\n| **Settings** | `settings.h/cpp` | EEPROM persistence, configuration storage and retrieval |\n| **PrintCounter** | `printcounter.h/cpp` | Print time, filament usage, and job statistics |\n| **Servo** | `servo.h/cpp` | Servo control for solenoids, lid mechanisms |\n| **Delta/Scara/Polar** | `delta.h`, `scara.h`, `polar.h` | Kinematic transforms for non-Cartesian printers |\n| **Tool Change** | `tool_change.h/cpp` | Multi-extruder tool switching, hotend offset, PTFE purge |\n\n### 4.4 Feature System\n\nThe `feature/` directory contains 80+ optional modules, each gated by a `#if ENABLED(FEATURE_NAME)` preprocessor check:\n\n**Motion Features:** Linear Advance (`linearadvance`), Pressure Advance, Adaptive Multi-Axis Stepping, Direct Stepping, Bresenham acceleration, Resonance Compensation, X-Axis Twist Correction, Z-Stepper Alignment, Backlash Compensation.\n\n**Thermal Features:** Automatic PID Tuning (M303), Thermal Protection, Probe Temperature Compensation, Mixed Extruder (`mixing`), Bowden/Direct Drive Retraction (`fwretract`).\n\n**Peripheral Features:** Power Loss Recovery (`powerloss`), Filament Runout Detection (`runout`), Ethernet Connectivity (`ethernet`), RS485 Communication (`rs485`), MMU/Multi- Material Unit (`mmu`, `mmu3`), Solenoid Control (`solenoid`), Spindle/Laser (`spindle_laser`), Case Lights (`caselight`), LED Color Control (`leds`).\n\n**UI Features:** Touch Screen (`touch`), LVGL TFT UI (`mks_ui`), DWIN Display (`dwin`), Extensible UI (`extui`), Password Protection (`password`), Joystick Input (`joystick`).\n\n**Bed Leveling:** Unified Bed Leveling (UBL), Manual Bed Leveling (MBL), Automatic Bed Leveling (ABL), G26 Mesh Validation, G35 Auto Bed Leveling, Bed Level Visualizer.\n\n## 5. G-Code Command Reference\n\nMarlin implements 100+ G-code commands organized into 14 categories under `src/gcode/`:\n\n| Category | Directory | Key Commands | Description |\n| --- | --- | --- | --- |\n| **Motion** | `motion/` | G0, G1, G2, G3, G5, G6, G80, M400, M290 | Linear/arc moves, rapid moves, dwell, wait, jog |\n| **Temperature** | `temp/` | M104/M105/M106/M107/M109, M140/M190, M303 | Hotend/bed temp set/read, fan control, PID autotune |\n| **Configuration** | `config/` | M200-M205, M301, M92, M43, M218, M217 | Extruder steps, accel/jerk, PID, hotend offset, bowden length |\n| **Calibration** | `calibrate/` | G28, G33, G34, G425, M48, M566, M665 | Home, delta calibration, nozzle purge, backlash, bellows |\n| **Geometry** | `geometry/` | G17-G19, G53-G59, G92, M206, M428 | Plane selection, coordinate systems, position reset, offsets |\n| **Control** | `control/` | M17/M18/M84, M80/M81, M3-M5, M7-M9, T | Enable axes, power, spindle, tool select, feedrate/extruder units |\n| **Probe** | `probe/` | G30, G31/G32, G38, M851, M401/M402, M102 | Bed probe, probe type, probe actions |\n| **SD Card** | `sd/` | M20-M34, M928, M1001, M1003 | File listing, select, start, stop, abort, load, save |\n| **LCD** | `lcd/` | M0, M1, M73, M117, M145, M250, M300 | Pause, progress, message, color, beep |\n| **Host** | `host/` | M16, M110, M113, M114, M115, M118, M119, M154, M360, M876 | Line numbers, machine info, endstop report, kinematic config |\n| **EEPROM** | `eeprom/` | M500, M501, M502, M503, M504 | Save/load/reset/settings EEPROM operations |\n| **Stats** | `stats/` | M31, M75-M78 | Print time, filament used, errors, buffer stats |\n| **Units** | `units/` | G20/G21, M82/M83, M149 | mm/inch, extrusion mode, temp unit display |\n| **OTA** | `ota/` | M936 | Over-the-air firmware update |\n\n## 6. Infrastructure & Deployment\n\n### 6.1 Build System\n\nMarlin uses PlatformIO as its primary build system, configured via `platformio.ini`:\n\n- **Framework**: Arduino (via PlatformIO)\n- **Build flags**: `-g3 -D__MARLIN_FIRMWARE__ -DNDEBUG -fsingle-precision-constant`\n- **Source filter**: Dynamic inclusion/exclusion of files based on target board via pre-build scripts (`configuration.py`, `common-dependencies.py`, `preflight-checks.py`)\n- **Include directory**: `Marlin/src/`\n- **Board definitions**: Custom PlatformIO board definitions in `buildroot/share/PlatformIO/boards/`\n\n### 6.2 Configuration System\n\nConfiguration is managed through a layered preprocessor conditional system:\n\n1. **`Configuration.h`** — User-editable hardware and feature configuration\n2. **`Configuration_adv.h`** — Advanced/experimental feature toggles\n3. **`Version.h`** — Build version, machine name, website URL\n4. **`inc/Conditionals-*.h`** — Auto-generated feature flags derived from Configuration.h\n5. **`inc/MarlinConfig.h`** — Prefix header including all conditionals, types, and sanity checks\n\nThe conditional system ensures that only enabled features compile, minimizing firmware footprint for resource-constrained 8-bit platforms.\n\n### 6.3 CI/CD Pipeline\n\nGitHub Actions workflows run on every pull request and push:\n\n| Workflow | Purpose |\n| --- | --- |\n| `ci-build-tests.yml` | Compiles Marlin for all supported boards (matrix strategy) |\n| `ci-unit-tests.yml` | Runs C++ unit tests on the native simulator |\n| `ci-validate-boards.yml` | Validates board pin configurations |\n| `ci-validate-pins.yml` | Checks for missing/invalid pin definitions |\n| `ci-validate-lines.yml` | Validates line count limits for 8-bit boards |\n| `check-pr.yml` | PR quality checks |\n\n### 6.4 Docker Build Environment\n\nA Dockerfile (`docker/Dockerfile`) provides a reproducible build environment:\n\n```dockerfile\nFROM python:3.11-bookworm\nRUN pip install -U platformio PyYaml\nWORKDIR /code\n```\n\n### 6.5 Testing\n\n- **Unit tests**: Located in `Marlin/tests/`, run via the Linux native simulator HAL\n- **Build tests**: CI compiles against 20+ board targets to catch compile errors\n- **Pin validation**: Automated checks for pin conflicts and missing definitions\n- **SdFat**: Integrated SdFat library for SD card file operations (Sd2Card, SdBaseFile, SdVolume)\n\n## 7. Extension Patterns\n\n### 7.1 Adding a New Feature\n\n1. Create your feature files in `Marlin/src/feature/` (e.g., `my_feature.h` and `my_feature.cpp`)\n2. Add `#define MY_FEATURE` to `Configuration_adv.h`\n3. The conditional system in `inc/Conditionals-*.h` will auto-generate `HAS_MY_FEATURE`\n4. Use `#if ENABLED(MY_FEATURE)` guards in your code\n5. Include your header from `MarlinCore.cpp` under the appropriate `#if ENABLED()` block\n6. Register G-code handlers in `gcode/gcode.cpp` or a new category subdirectory\n\n### 7.2 Adding a New G-Code Command\n\n1. Create a new file in the appropriate `src/gcode/<category>/` directory (e.g., `M1000.cpp`)\n2. Implement the handler method in the `GcodeSuite` class\n3. Register the command in the `process_commands()` method of `GcodeSuite`\n4. The command will be dispatched automatically when the parser encounters the letter/code\n\n### 7.3 Adding Board Support\n\n1. Create a new PlatformIO board definition in `buildroot/share/PlatformIO/boards/`\n2. Add pin definitions in `Marlin/src/pins/<family>/pins_<board>.h`\n3. If the MCU is new, add HAL support in `Marlin/src/HAL/<MCU_FAMILY>/`\n4. Update `ini/<platform>.ini` in `platformio.ini`'s `extra_configs`\n5. Run `ci-validate-pins.yml` to verify pin assignments\n\n### 7.4 Adding a Display Backend\n\n1. Create backend files in `Marlin/src/lcd/<backend>/`\n2. Implement the required display interface (SPI/I2C/UART)\n3. Add `#define ULTIPANEL` or display-specific `#define` to `Configuration.h`\n4. The LCD subsystem routes through `MarlinUI` to the appropriate backend\n\n## 8. Rules & Anti-Patterns\n\n### Best Practices\n\n- **Conditional compilation**: Always gate platform-specific code with `#if ENABLED()` or `#ifdef ARDUINO_ARCH_XXX`\n- **PROGMEM usage**: Store large strings and lookup tables in program memory on 8-bit platforms\n- **Interrupt safety**: Use `WAIT`/`NOOP` patterns for stepper ISR synchronization\n- **EEPROM limits**: Respect memory constraints — 8-bit boards have limited EEPROM (4KB-8KB)\n- **Thread safety**: The main loop and stepper ISR share state — use volatile and careful locking\n- **Static analysis**: Use `bug_on()` macros for compile-time assertion checks\n- **Code size**: 8-bit targets (AVR) have strict flash limits — enable only necessary features\n\n### Anti-Patterns\n\n- **Don't add features to `Configuration.h`** — use `Configuration_adv.h` for advanced/experimental features\n- **Don't modify HAL shared code** without understanding platform implications\n- **Don't use floating-point on AVR** without explicit `float` typing — the compiler defaults to single precision\n- **Don't block in the main loop** — all long operations must yield to `idle()` or use non-blocking patterns\n- **Don't assume 32-bit semantics** — code must compile for both 8-bit AVR and 32-bit ARM\n- **Don't hardcode pin numbers** — always use the pin definition headers\n\n## 9. Dependencies\n\n### Build Tools\n\n| Dependency       | Version           | Purpose                    |\n| ---------------- | ----------------- | -------------------------- |\n| PlatformIO       | Latest dev        | Build system and framework |\n| Arduino Core     | Platform-specific | Microcontroller SDK        |\n| GCC ARM Embedded | 12.x              | ARM cross-compiler         |\n| avr-gcc          | 7.x               | AVR cross-compiler         |\n\n### Libraries (integrated)\n\n| Library    | Purpose                                             |\n| ---------- | --------------------------------------------------- |\n| SdFat      | SD card file system (Sd2Card, SdBaseFile, SdVolume) |\n| u8glib     | OLED/LCD display driver (embedded in HAL)           |\n| LVGL       | TFT graphical UI (optional, for MKS UI)             |\n| heatshrink | G-code decompression for SD prints                  |\n\n### Platform Libraries\n\n| Platform | Libraries                     |\n| -------- | ----------------------------- |\n| STM32    | STM32 HAL/LL, STM32duino core |\n| AVR      | Arduino AVR core, TimerOne    |\n| ESP32    | ESP-IDF components, WiFi      |\n| RP2040   | Arduino RP2040 core, PIOasm   |\n| Teensy   | Teensyduino core, Bounce2     |\n| SAMD     | Arduino SAMD core             |\n\n## 10. Code Structure\n\n```\nMarlinFirmware/                  # Repo root (this directory). `cd` here for PlatformIO builds.\n├── Marlin/                      # The Arduino \"sketch\" (application source + config files)\n│   ├── Configuration.h          # User hardware configuration\n│   ├── Configuration_adv.h      # Advanced/experimental toggles\n│   ├── Marlin.ino               # Board-specific entry stub\n│   ├── Makefile                 # Alternative build (simulator)\n│   ├── Version.h                # Build version, machine name\n│   ├── config.ini               # PlatformIO extra config\n│   ├── lib/                     # Integrated libraries\n│   └── src/                     # Application source (see below)\n├── buildroot/                   # Build infrastructure\n├── ini/                         # PlatformIO platform configs\n├── docs/                        # Project documentation\n├── docker/                      # Docker build environment\n├── .github/workflows/           # CI/CD pipelines\n└── platformio.ini               # Main PlatformIO configuration\n```\n\n### Marlin/src layout\n\n```\nMarlin/src/\n├── MarlinCore.cpp/h         # Firmware entry, setup(), loop(), Marlin singleton\n├── core/                    # Core utilities (serial, language, types, mstring)\n│   ├── gcode/                   # G-code subsystem\n│   │   ├── gcode.cpp/h          # GcodeSuite dispatcher\n│   │   ├── parser.cpp/h         # GCodeParser\n│   │   ├── queue.cpp/h          # GCodeQueue ring buffer\n│   │   ├── motion/              # G0-G6, M290, M400\n│   │   ├── temp/                # M104-M109, M140-M193, M303\n│   │   ├── config/              # M200-M205, M301, M92, M218\n│   │   ├── calibrate/           # G28, G33, G34, G425, M48\n│   │   ├── bedlevel/            # G26, G35, G42, M420\n│   │   ├── geometry/            # G17-G19, G53-G59, G92\n│   │   ├── probe/               # G30-G38, M851, M401\n│   │   ├── sd/                  # M20-M34, M928\n│   │   ├── lcd/                 # M0, M1, M73, M117\n│   │   ├── host/                # M16, M110-M119\n│   │   ├── eeprom/              # M500-M504\n│   │   ├── stats/               # M31, M75-M78\n│   │   ├── units/               # G20/G21, M82/M83\n│   │   └── control/             # M3-M5, M7-M9, M17-M85, T\n│   ├── module/                  # Core modules\n│   │   ├── motion.h/cpp         # Axis positioning, kinematics\n│   │   ├── planner.h/cpp        # Block buffer, trapezoidal profiling\n│   │   ├── stepper.h/cpp        # Step pulses, TMC drivers\n│   │   ├── temperature.h/cpp    # PID, thermistors, safety\n│   │   ├── endstops.h/cpp       # Endstop interrupts, homing\n│   │   ├── probe.h/cpp          # Bed probing, Z-offset\n│   │   ├── settings.h/cpp       # EEPROM persistence\n│   │   ├── delta.h/cpp          # Delta kinematics\n│   │   ├── scara.h/cpp          # Scara kinematics\n│   │   ├── polar.h/cpp          # Polar kinematics\n│   │   ├── polargraph.h/cpp     # Polar graph kinematics\n│   │   ├── servo.h/cpp          # Servo control\n│   │   ├── printcounter.h/cpp   # Print statistics\n│   │   ├── tool_change.h/cpp    # Multi-extruder switching\n│   │   ├── ft_motion/           # Fine-tune motion\n│   │   └── stepper/             # Stepper internals, Trinamic\n│   ├── feature/                 # 80+ optional features\n│   │   ├── bedlevel/            # UBL, MBL, ABL\n│   │   ├── leds/                # LED color control\n│   │   ├── mmu/                 # Multi-material unit\n│   │   ├── mmu3/                # MMU3 variant\n│   │   ├── powerloss.h/cpp      # Power loss recovery\n│   │   ├── runout.h/cpp         # Filament runout detection\n│   │   ├── pause.h/cpp          # Pause/resume\n│   │   ├── mixing.h/cpp         # Mixed extruder\n│   │   ├── fwretract.h/cpp      # Firmware retraction\n│   │   ├── resonance/           # Resonance compensation\n│   │   ├── spindle_laser.h/cpp  # Spindle/laser control\n│   │   ├── ethernet.h/cpp       # Ethernet connectivity\n│   │   ├── solenoid.h/cpp       # Solenoid control\n│   │   ├── password/            # Password protection\n│   │   ├── joystick.h/cpp       # Joystick input\n│   │   ├── tmc_util.h/cpp       # TMC driver utilities\n│   │   └── ...                  # 60+ more features\n│   ├── lcd/                     # Display/UI subsystem\n│   │   ├── marlinui.h/cpp        # Main UI controller\n│   │   ├── HD44780/             # Hitachi HD44780 LCD\n│   │   ├── dogm/                # DOGM OLED/LCD\n│   │   ├── tft/                 # TFT displays\n│   │   ├── tft_io/              # TFT I/O drivers\n│   │   ├── dwin/                # DWIN displays\n│   │   ├── extui/               # Extensible UI (MKS LVGL)\n│   │   ├── menu/                # Menu system\n│   │   ├── touch/               # Touch screen\n│   │   ├── language/            # Multi-language strings\n│   │   └── sovol_rts/           # Sovol RTS display\n│   ├── libs/                    # Utility libraries\n│   │   ├── bresenham.h          # Bresenham line algorithm\n│   │   ├── vector_3.h/cpp       # 3D vector math\n│   │   ├── circularqueue.h      # Generic circular queue\n│   │   ├── adc/                 # ADC utilities\n│   │   ├── heatshrink/          # Decompression\n│   │   ├── nozzle.cpp/h         # Nozzle cleaning\n│   │   ├── numtostr.h/cpp       # Number-to-string\n│   │   ├── hex_print.h/cpp      # Hex printing\n│   │   ├── crc16.h/cpp          # CRC-16 calculation\n│   │   ├── stopwatch.h/cpp      # Stopwatch utility\n│   │   ├── least_squares_fit.h  # Least squares fitting\n│   │   └── ...                  # 15+ utility libs\n│   ├── sd/                      # SD card subsystem\n│   │   ├── cardreader.h/cpp     # Card reader interface\n│   │   ├── SdFat*               # SdFat library (file system)\n│   │   ├── disk_io_driver.h     # Disk I/O abstraction\n│   │   └── usb_flashdrive/      # USB flash drive support\n│   ├── pins/                    # Pin definitions (400+ files)\n│   │   ├── pins.h               # Pin inclusion router\n│   │   ├── ramps/               # Ramps board family\n│   │   ├── mega/                # Mega board family\n│   │   ├── rambo/               # Rambo board family\n│   │   ├── sanguino/            # Sanguino board family\n│   │   ├── stm32f1/             # STM32F1 boards\n│   │   ├── stm32f4/             # STM32F4 boards\n│   │   ├── stm32f7/             # STM32F7 boards\n│   │   ├── stm32h7/             # STM32H7 boards\n│   │   ├── stm32g0/             # STM32G0 boards\n│   │   ├── stm32f0/             # STM32F0 boards\n│   │   ├── sam/                 # SAM boards\n│   │   ├── samd/                # SAMD boards\n│   │   ├── rp2040/              # RP2040 boards\n│   │   ├── lpc1768/             # LPC1768 boards\n│   │   ├── lpc1769/             # LPC1769 boards\n│   │   ├── teensy2/             # Teensy 2.x boards\n│   │   ├── teensy3/             # Teensy 3.x boards\n│   │   ├── teensy4/             # Teensy 4.x boards\n│   │   ├── esp32/               # ESP32 boards\n│   │   ├── gd32f1/              # GD32F1 boards\n│   │   ├── gd32f3/              # GD32F3 boards\n│   │   ├── hc32f4/              # HC32F4 boards\n│   │   ├── at32f4/              # AT32F4 boards\n│   │   └── native/              # Native/simulator\n│   ├── HAL/                     # Hardware Abstraction Layer\n│   │   ├── HAL.h                # HAL interface definition\n│   │   ├── platforms.h          # Platform selection\n│   │   ├── shared/              # Cross-platform HAL code\n│   │   │   ├── HAL.cpp/h        # Shared HAL implementation\n│   │   │   ├── Delay.h          # Safe delay function\n│   │   │   ├── eeprom_api.h     # EEPROM API\n│   │   │   ├── cpu_exception/   # Exception handling\n│   │   │   └── backtrace/       # Stack backtrace\n│   │   ├── STM32/               # STM32 platform HAL\n│   │   ├── AVR/                 # AVR platform HAL\n│   │   ├── ESP32/               # ESP32 platform HAL\n│   │   ├── DUE/                 # Arduino Due HAL\n│   │   ├── SAMD21/              # SAMD21 HAL\n│   │   ├── SAMD51/              # SAMD51 HAL\n│   │   ├── RP2040/              # RP2040 HAL\n│   │   ├── LPC1768/             # LPC1768 HAL\n│   │   ├── GD32_MFL/            # GD32 HAL\n│   │   ├── HC32/                # HC32 HAL\n│   │   ├── AT32/                # AT32 HAL\n│   │   ├── TEENSY31_32/         # Teensy 3.1/3.2 HAL\n│   │   ├── TEENSY35_36/         # Teensy 3.5/3.6 HAL\n│   │   ├── TEENSY40_41/         # Teensy 4.0/4.1 HAL\n│   │   ├── STM32F1/             # STM32F1 HAL\n│   │   ├── LINUX/               # Linux simulator HAL\n│   │   └── NATIVE_SIM/          # Native simulator HAL\n│   └── tests/                   # Unit tests\n│       ├── unit_tests.h/cpp     # Test suite\n│       └── *.ini                # Test configurations\n├── docs/                        # Project documentation\n│   ├── AGENTS.md                # This document\n│   └── diagrams/                # Architecture diagrams\n│       ├── high-level-architecture.drawio\n│       ├── processing-pipeline.drawio\n│       └── component-relationships.drawio\n├── buildroot/                   # Build infrastructure\n│   ├── share/PlatformIO/        # PlatformIO board definitions & scripts\n│   ├── test-gcode/              # G-code test files\n│   └── tests/                   # Build test configurations\n├── ini/                         # PlatformIO platform configs\n│   ├── avr.ini, due.ini, esp32.ini\n│   ├── stm32-common.ini, stm32f1.ini, stm32f4.ini\n│   ├── stm32f7.ini, stm32h7.ini, stm32g0.ini\n│   ├── samd21.ini, samd51.ini\n│   ├── raspberrypi.ini, teensy.ini\n│   ├── at32.ini, gd32.ini, hc32.ini\n│   ├── lpc176x.ini, native.ini\n│   ├── features.ini, renamed.ini\n│   └── stm32f1-maple.ini\n├── test/                        # Root-level test configurations\n├── docker/                      # Docker build environment\n├── .github/workflows/           # CI/CD pipelines\n└── platformio.ini               # Main PlatformIO configuration\n```\n\n## 11. Constructing a Pull Request\n\nMarlin accepts AI-assisted code (see `.github/contributing.md`). It must be sensible and reviewable. Follow these rules when preparing a PR.\n\n### Scope and size\n\n- **One PR, one purpose.** A PR should be a single self-contained change that builds and passes CI on its own.\n- **Split large changes.** If a change touches several unrelated areas, or is large enough that a reviewer can't hold it in their head, split it into two (or more) smaller PRs. Typical splits: refactor first, feature second; core logic first, UI/LCD support second; one HAL or board family per PR; sanity checks and docs with the feature they cover.\n- **Order dependent PRs.** If PR B needs PR A, say so in B's description and don't open B until A is far enough along to be reviewed.\n- **No drive-by changes.** Don't reformat, rename, or tidy code you aren't changing. Keep the diff minimal.\n- **Don't change defaults** in `Configuration.h` / `Configuration_adv.h` unless that is the purpose of the PR. New features are disabled by default.\n\n### Before you start\n\n- Branch from the latest `bugfix-2.1.x` (the working branch). Never target release branches.\n- We occasionally backport fixes to lts-* branches. Very small non-duplicate patches are accepted.\n- Read the neighboring code and follow its conventions. Check `buildroot/skills/` for step-by-step procedures.\n- Read the HAL-specific `AGENTS.md` (`Marlin/src/HAL/<FAMILY>/AGENTS.md`) when touching a HAL.\n\n### Making the change\n\n- Code must compile on 8-bit AVR and 32-bit targets. Mind flash and RAM on AVR. Keep the base build requirements from growing.\n- Gate optional code with `ENABLED()` / `DISABLED()` / `ANY()` / `NONE()` / etc. and add validation to `Marlin/src/inc/SanityCheck.h`.\n- Use a suitable insertion point for Configuration and Conditionals. They follow a strict order set by `Marlin/src/inc/MarlinConfig.h`.\n- Don't hardcode pins; use the pin headers in `src/pins`.\n- When adding or changing a user-facing option, update its comments in `Configuration*.h` and note any `Conditionals`, `Warnings.cpp`, or `config.ini` impacts.\n- Follow the Marlin coding standards (2-space indent, no tabs, spacing as in nearby code). Indent new code similar to surroundings (including preprocessor nesting) and avoid auto-formatters.\n\n### Verifying\n\n- Use `MarlinSimUI` for direct testing in a simulated environment with a rich MCP interface. (And contribute your enhancements to `MarlinSimUI`.)\n- Build at least one affected environment (`buildroot/bin/mftest`; see the `marlin-build-test` skill).\n- **Add tests with new features and options.** Add or update a `buildroot/tests/<env>/config-NN.ini` build test so CI exercises the feature, along with its dependent options.\n- Add a unit test in `Marlin/tests/<area>/` (run with `make unit-test-all-local`) when the logic can be checked without hardware.\n  - Bug fixes should get a test when one can catch the regression. See the `marlin-build-test` skill.\n- Report honestly what was and wasn't tested. Never claim a build or test passed unless you ran it.\n\n### Commit messages and PR description\n\n- Imperative mood, first line of 72 characters or fewer, prefixed with a gitmoji-style emoji as in `git log` (e.g. 🐛 fix, ✨ feature, ⚡️ performance, 🔧 config, 🩹 minor fix, 🧑‍💻 developer tooling, 🚸 UX).\n- Don't put issue numbers in the PR title. Reference them in the description.\n- Fill in every section of `.github/pull_request_template.md`: Description, Requirements, Benefits, Configurations, Related Issues.\n- Keep history clean: rebase on `bugfix-2.1.x` rather than merging, and squash fixups into the commits they belong to.\n\n### Skills\n\nReusable procedures for contributors and their tools live in `buildroot/skills/`. Each skill is a folder with a `SKILL.md`. See `buildroot/skills/README.md`.\n","isInternal":false,"tokens":8685,"sizeBytes":34310},{"name":"SKILL.md","path":"buildroot/skills/marlin-build-test/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-build-test/SKILL.md","title":"marlin-build-test","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-build-test\ndescription: Use when you need to compile Marlin for a board, apply a CI test configuration, or run the build tests locally. Wraps buildroot/bin/mftest, mfenvs, run_tests, and the Makefile targets.\n---\n\n# Build and Test Marlin\n\nRequires PlatformIO (`pio`) in your `PATH`. Run everything from the repo root.\n\n## Build the configured board\n\n1. Set `MOTHERBOARD` in `Marlin/Configuration.h` and set up your options.\n2. List the PlatformIO environments for the board: `buildroot/bin/mfenvs`\n3. Build the auto-detected environment: `buildroot/bin/mftest -a` (rebuild with `-r`, silent with `-s`).\n4. Or build one environment directly: `pio run -e <env>`\n\n## Apply a CI test config and build it\n\nTests live in `buildroot/tests/<env>/config-NN.ini` and are applied with the `opt_*` tools.\n\n- Interactive menu: `buildroot/bin/mftest`\n- Specific test: `buildroot/bin/mftest -t mega2560 -n 5 -y` (`-t` env name or shortcut, `-n` test index, `-y` build without asking)\n- Start from default configs first: add `-d`\n- Only emit the config changes without building: add `-i`\n- Use the Docker/make method: add `-m`\n\nShortcuts for `-t`: `tree due esp lin lp8 lp9 m128 mega stm f1 f4 f7 s6 teensy t31 t35 t40`.\n\n## Add tests for a new feature or option\n\nA PR that adds a feature or option should add tests, so CI keeps it building and working.\n\n**Build test** (`buildroot/tests/<env>/config-NN.ini`)\n\n1. Pick the env directory that matches a board and platform that can build the feature. Use `buildroot/bin/mfenvs` or the existing directories under `buildroot/tests`.\n2. Prefer adding the option to an existing `config-NN.ini` with a similar feature set. Add a new file only for a combination that no existing test covers. Use the next free number.\n3. Each file begins with a header (`# Test: <env>/config-NN.ini`, then a one-line description of the main options) and a `[config:base]` section. Start from an example with `ini_use_config = example/...`, or leave it out for the defaults.\n4. Set options as `name = value` or `name = on`. Test the feature enabled along with the options it depends on, and with the dependent sub-options.\n5. Run just your test: `buildroot/bin/mftest -t <env> -n <index> -y` or `make tests-single-local TEST_TARGET=<env> ONLY_TEST=<index>`, where `<index>` is the test's position in file order (1-based) and normally matches `NN`.\n6. Keep the number of tests small. Every test adds to CI time.\n\n**Unit test** (when the logic can be checked without hardware)\n\nUnit tests run natively on the `linux_native_test` environment. Good candidates are parsing, math, conversions, state machines and G-code handling. Anything that needs real pins, timers, or devices isn't a good candidate. See `test/README.md`.\n\n1. Add `Marlin/tests/<area>/test_<name>.cpp`. Copy the license header and the `MARLIN_TEST(group, name)` macros from an existing test such as `Marlin/tests/feature/test_runout.cpp`.\n2. Wrap the file's contents in `#if ENABLED(FEATURE)` so it compiles only for configs that enable it.\n3. If no existing config in `test/*.ini` enables your feature, add one (`test/NNN-name.ini`). It must use `motherboard = BOARD_SIMULATED` and include only the options needed to pass the sanity checks.\n4. Run: `make unit-test-all-local`, or one config with `make unit-test-single-local UNIT_TEST_CONFIG=<name>`. (There is a `-docker` variant of each.)\n\n## Run the tests like CI does\n\n- One environment: `make tests-single-local TEST_TARGET=mega2560`\n- One test of it: `make tests-single-local TEST_TARGET=mega2560 ONLY_TEST=2`\n- Everything (slow): `make tests-all-local`\n- In Docker: `make tests-single-local-docker TEST_TARGET=mega2560` (see `make help`)\n- Unit tests: `make unit-test-all-local`\n\nThe test runner restores the configs between tests and may `git reset --hard` (see `GIT_RESET_HARD` in `make help`), so commit or stash your work first.\n\n## Restore configuration files\n\nAfter testing, run `buildroot/bin/restore_configs`. It deletes generated files (`marlin_config.json`, `_Bootscreen.h`, `_Statusscreen.h`) and does a `git checkout` of the configuration files, discarding any uncommitted edits to them. `restore_configs -d` instead loads the default example configs.\n\n## Choosing what to build\n\n- Build at least one environment each for any HAL or board family you changed.\n- A change to common code deserves an AVR build (tight flash) and a 32-bit build.\n- For features, build with the feature both enabled and disabled.\n- New feature or option: add a build test and, where it makes sense, a unit test (see above).\n\n## Report results\n\nState exactly which envs and tests you built. Do not claim a pass that you did not see.\n","frontmatter":{"name":"marlin-build-test","description":"Use when you need to compile Marlin for a board, apply a CI test configuration, or run the build tests locally. Wraps buildroot/bin/mftest, mfenvs, run_tests, and the Makefile targets."},"isInternal":false,"tokens":1187,"sizeBytes":4668},{"name":"SKILL.md","path":"buildroot/skills/marlin-code-style/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-code-style/SKILL.md","title":"marlin-code-style","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-code-style\ndescription: Use before committing C/C++ changes to Marlin, to match project conventions and indentation in new code without reformatting existing code.\n---\n\n# Marlin Code Style\n\nFull standards: https://marlinfw.org/docs/development/coding_standards.html\n\n## Rules of thumb\n\n- Match the file you are editing: indentation (2 spaces, no tabs), brace placement, spacing, naming.\n- Code inside preprocessor blocks (`#if` / `#else` / `#endif`) is indented one level per nesting depth, the same as code inside braces. Copy how nearby code does it.\n- Use `ENABLED(FEATURE)` / `DISABLED(FEATURE)` for options, and `#if HAS_...` for derived capabilities.\n- Keep optional code behind its feature flag so it costs nothing when disabled.\n- Code must build on 8-bit AVR as well as 32-bit targets. Avoid unnecessary `double`, `String`, and heap allocation.\n- Comments should say why, concisely. End every file with a newline. No trailing whitespace.\n\n## Don't reformat existing code\n\n- Existing code may have ad hoc alignment and spacing (aligned columns, grouped one-liners). Leave it alone.\n- Don't re-indent, re-wrap, or re-space lines you aren't otherwise changing. Keep diffs small and focused.\n- Exception: It's good to keep related eol comments aligned.\n- Review `git diff` before committing and revert any whitespace-only churn.\n\n## Auto-formatters\n\nDon't run the auto-formatter on existing files. `buildroot/bin/format_code` and `uncrust` (uncrustify) get close to the Marlin style but don't match it exactly, and will produce unwanted churn. If you use one, apply it only to a scratch copy to see suggestions, then hand-apply what is correct to your own new lines.\n","frontmatter":{"name":"marlin-code-style","description":"Use before committing C/C++ changes to Marlin, to match project conventions and indentation in new code without reformatting existing code."},"isInternal":false,"tokens":395,"sizeBytes":1689},{"name":"SKILL.md","path":"buildroot/skills/marlin-codebase-navigation/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-codebase-navigation/SKILL.md","title":"marlin-codebase-navigation","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-codebase-navigation\ndescription: Use when you need to find where something is defined, what uses it, or how a command or setting flows through Marlin's source. Uses only git, grep, and the repo layout, with optional tools as extras.\n---\n\n# Navigate the Marlin Source\n\nMarlin is a large C++ tree where most code is compiled only under certain options. These steps work with nothing but `git` and a shell. Better tools are listed at the end as optional.\n\nFor the layout, singletons, and processing pipeline, read `AGENTS.md` (sections 2 to 4 and 10) first. This skill covers how to search, and where the common things live.\n\n## Where things live\n\n| Looking for | Look in |\n| --- | --- |\n| A G-code command `Mnnn` / `Gnnn` | `git grep -n \"GcodeSuite::M913\"`. Handlers are in `Marlin/src/gcode/<group>/`, and some files hold several (`M911-M914.cpp`). The dispatch `switch` is in `Marlin/src/gcode/gcode.cpp`, and declarations are in `gcode/gcode.h` |\n| What runs each loop | `setup()` and `loop()` in `Marlin/src/MarlinCore.cpp` |\n| Motion, planner, stepper ISR | `Marlin/src/module/` (`motion`, `planner`, `stepper`) |\n| Temperature, PID, MPC | `Marlin/src/module/temperature.cpp` |\n| Optional feature code | `Marlin/src/feature/` and `gcode/feature/` |\n| An option's derived flags (`HAS_*`) | `Marlin/src/inc/Conditionals-*.h` |\n| Why a config combination is rejected | `Marlin/src/inc/SanityCheck.h`; renamed options are in `Changes.h` |\n| A board's pins | `Marlin/src/pins/<family>/pins_<BOARD>.h`; the `#elif MB(...)` chain is in `pins/pins.h`; the ID is in `core/boards.h` |\n| Platform-specific code | `Marlin/src/HAL/<FAMILY>/`, and that folder's `AGENTS.md` |\n| An LCD string | `Marlin/src/lcd/language/language_en.h` (`MSG_*`) |\n| A menu screen | `Marlin/src/lcd/menu/menu_*.cpp` |\n| A serial or touch display | `Marlin/src/lcd/extui/` |\n| Which source folders a build includes | `ini/features.ini` and `ini/*.ini` |\n| A CI test config | `buildroot/tests/<env>/config-NN.ini` |\n\n## Search recipes\n\n    git grep -n \"OPTION_NAME\" -- Marlin                  # every use, including config comments\n    git grep -n \"SOME_OPTION\" -- 'Marlin/src/inc/*'      # where it is derived or validated\n    git grep -nE \"\\bfunction_name\\(\" -- Marlin/src       # calls and definitions\n    git grep -n \"STR_SOMETHING\\|MSG_SOMETHING\" -- Marlin/src/lcd\n    git log -S\"suspicious_string\" --oneline -- Marlin/src   # when it was introduced or removed\n    git log --oneline -20 -- Marlin/src/module/motion.cpp   # recent changes to a file\n    git blame -L 120,160 Marlin/src/module/motion.cpp       # who changed these lines, then read that PR\n\n## Tracing a setting or command\n\n1. Find the option in `Marlin/Configuration.h` or `Configuration_adv.h`.\n2. `git grep` it under `Marlin/src/inc` to see what it enables and what it conflicts with.\n3. `git grep` it under the rest of `Marlin/src` to find the code it gates.\n4. For a G-code, go from the `case` in `gcode/gcode.cpp` to its handler, then follow the calls into `module/`.\n5. If a symbol seems to have no users, check for macro use (`TERN`, `ENABLED`) and for different spellings under other options before deciding it is dead.\n\n## Pitfalls\n\n- **Most code is conditional.** A match inside `#if ENABLED(X)` is not compiled unless `X` is on. Read the enclosing `#if`s, and build with the option on and off.\n- **Macros hide calls.** `SERIAL_ECHO*`, `TERN*`, `LOOP_*`, and `HAL_PATH` includes do not look like ordinary function calls, so search for the macro name too.\n- **Old issues and forks use old paths.** `ultralcd.*` is now `marlinui.*`, and `Conditionals_post.h` was split into the numbered files. Search the current name before applying advice from an old thread.\n- **Line numbers drift.** Quote a symbol and a file, not only a line number, in a PR or comment.\n- **IDE diagnostics can mislead.** Marlin builds with GCC. A Clang-based language server often reports errors on headers that build fine. Trust the build (skill: marlin-build-test).\n\n## Optional tools\n\nNone of these are required, and none replace reading the code.\n\n- `rg` (ripgrep) is faster than `git grep` on the whole tree and handles the same patterns.\n- Your AI tool's own search (for example Claude Code's file and content search, or a code-exploration sub-agent) works well for broad questions such as \"where is homing decided\". Check its answer with `git grep`.\n- A symbol index such as `universal-ctags` helps with jump-to-definition in an editor. Because of the `#if` blocks it can show several definitions of one name.\n- A code-knowledge-graph MCP server (for example codebase-memory-mcp) can locate definitions, line ranges, and function size from a natural-language query. In testing on Marlin, its call-graph results were incomplete: callers came back at file level and macro-dispatched code had no callees. Use it to locate code and `git grep` to find every use. It indexes a snapshot of the checked-out tree, so re-index after switching branches.\n- If you try a language-server plugin, expect noise from the heavy preprocessor use and keep a real build as the final check.\n","frontmatter":{"name":"marlin-codebase-navigation","description":"Use when you need to find where something is defined, what uses it, or how a command or setting flows through Marlin's source. Uses only git, grep, and the repo layout, with optional tools as extras."},"isInternal":false,"tokens":1351,"sizeBytes":5110},{"name":"SKILL.md","path":"buildroot/skills/marlin-config-options/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-config-options/SKILL.md","title":"marlin-config-options","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-config-options\ndescription: Use when you need to find, enable, disable, or set Marlin configuration options (Configuration.h / Configuration_adv.h) from the command line. Wraps buildroot/bin/opt_find, opt_enable, opt_disable, opt_set, and opt_add.\n---\n\n# Marlin Configuration Options from the Command Line\n\nThese scripts edit `Marlin/Configuration.h` and `Marlin/Configuration_adv.h` in place. Run them from the repo root. Commit or stash your own edits first so the changes are easy to revert, or run `buildroot/bin/restore_configs` afterward (which does a `git checkout` of the config files, discarding uncommitted edits to them).\n\n## Find\n\n    buildroot/bin/opt_find LIN_ADV         # enabled or disabled options matching the name\n    buildroot/bin/opt_find -e BABYSTEP     # only enabled\n    buildroot/bin/opt_find -d BABYSTEP     # only disabled\n\n## Enable / disable\n\n    buildroot/bin/opt_enable LIN_ADVANCE ADAPTIVE_STEP_SMOOTHING\n    buildroot/bin/opt_disable ADAPTIVE_STEP_SMOOTHING\n\n`opt_enable` exits with an error if an option can't be found.\n\n## Set a value\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_RAMPS_14_EFB EXTRUDERS 2\n\nTakes `<name> <value>` pairs. An option that does not exist is added to `Configuration.h`.\n\n## Use example configurations\n\n    buildroot/bin/use_example_configs Creality/CR-10/CrealityV1\n\nFetches the named example from the Configurations repo (branch matched to your working branch, otherwise `bugfix-2.1.x`). Use `release-2.0.9.4:Creality/CR-10/CrealityV1` to pick a branch or tag.\n\n## Tips\n\n- Option names are matched exactly by enable/disable/set. Use `opt_find` first.\n- Some options need others (see `Marlin/src/inc/SanityCheck.h`). A build error from a sanity check tells you what is missing.\n- The CI test files in `buildroot/tests/` use these same operations. Read a few as examples.\n","frontmatter":{"name":"marlin-config-options","description":"Use when you need to find, enable, disable, or set Marlin configuration options (Configuration.h / Configuration_adv.h) from the command line. Wraps buildroot/bin/opt_find, opt_enable, opt_disable, opt_set, and opt_add."},"isInternal":false,"tokens":461,"sizeBytes":1851},{"name":"SKILL.md","path":"buildroot/skills/marlin-extui-backends/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-extui-backends/SKILL.md","title":"marlin-extui-backends","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-extui-backends\ndescription: Use when adding or changing an ExtUI display backend (DGUS, Anycubic, Nextion, Malyan, FTDI EVE, Neptune 3, IA Creality, serial panels) or the ExtUI API in ui_api.h / ui_api.cpp.\n---\n\n# Marlin ExtUI Backends\n\nExtUI (`Marlin/src/lcd/extui/ui_api.{h,cpp}`) is the API used by serial and touch displays. Backends live in subfolders of `Marlin/src/lcd/extui/`. `example/example.cpp` is the template for a new one.\n\n## Rules\n\n1. **Event handlers (`ExtUI::on*()`) have weak, empty defaults in `ui_api.cpp`.** A backend defines only the handlers it uses. Don't add empty `{}` stubs to a backend, and delete them if you see them. A new handler declared in `ui_api.h` gets exactly one weak empty default in the `WEAK_EXTUI` block in `ui_api.cpp`, inside the same `#if` guard as its declaration. No backend needs to change.\n2. **API functions (`get*` / `set*`) are not event handlers.** They get one real implementation in `ui_api.cpp`, next to the matching getter, and are never weak or inside a backend. A setter moved into the backends leaves empty stubs that silently do nothing.\n3. **Keep the `ExtUI` namespace flat.** Don't use the `using namespace Base` override pattern from `language_*.h`: `ui_api.h` already declares every handler in `ExtUI`, and name hiding breaks overloads such as `onUserConfirmRequired` and `onMeshUpdate`.\n4. **Backends keep to their own folder.** Add the folder to `ini/features.ini` (`build_src_filter=+<src/lcd/extui/<folder>>`), or it will be silently excluded and the link fails with every callback undefined.\n5. **Wire the new LCD define in `Conditionals-2-LCD.h`** (the `IS_EXTUI` list, the `LCD_IS_SERIAL_HOST` list if it is a serial display, and the \"only one LCD\" count), and add its checks to `SanityCheck.h`. Copy a similar backend's entries.\n6. **Handlers must match the current `ui_api.h` signatures.** Backends ported from forks often use renamed or removed handlers. Diff against the full callback list in `ui_api.h` before building.\n7. **After a bulk deletion, tidy up.** Remove runs of blank lines, a blank line directly after `#if` or before `#endif`, orphaned comments, and emptied `#if` blocks. Diff against the base and check that the only `+` lines are the ones you intended.\n\n## A panel that speaks G-code (PanelDue and similar)\n\nTreat it as an ExtUI backend that owns `LCD_SERIAL`, with the whole protocol inside the backend. Add it to `LCD_IS_SERIAL_HOST` (which auto-assigns `LCD_SERIAL_PORT`) rather than routing it through `SERIAL_PORT_2`.\n\n- `LCD_SERIAL` is never a G-code source. The queue reads only `NUM_SERIAL` ports, and `SERIAL_ECHO*` never reaches `LCD_SERIAL`. The backend must call `LCD_SERIAL.begin(LCD_BAUDRATE)` in `onStartup()`, read lines in `onIdle()`, and write replies to `LCD_SERIAL` itself, as Malyan does.\n- Answer status queries directly from `onIdle()`, never through the queue. `idle()` keeps running during `G28`, `M109`, and SD prints, so the panel stays connected while Marlin is busy.\n- Run other commands by injection. `queue.inject()` overwrites the single `injected_commands` buffer (64 bytes), so keep your own FIFO. Hand over whole lines only when `queue.injected_commands[0] == 0`. Send a line too long for the buffer with `queue.enqueue_one()` and retry if that fails.\n- Apply temperature, fan, speed, and flow changes with the ExtUI setters, so they take effect even during a blocking `M109`.\n- Buffer replies and drain them with `LCD_SERIAL_TX_BUFFER_FREE()` (`SERIAL_GET_TX_BUFFER_FREE()` on ESP32 and RP2040). A 1 KB reply takes about 180 ms at 57600 baud, which is too long to block `idle()` during a print.\n- Keep each JSON or status reply on one line. Clients such as PanelDue discard a partial object when they see a newline.\n- If the panel has its own buzzer, add it to the `USE_MARLINUI_BUZZER` list in `Conditionals-5-post.h` and define `MarlinUI::buzz()` in the backend. Without a `BEEPER_PIN`, `HAS_SOUND` is off and `M300` isn't compiled.\n\n## Verify\n\n1. Build every backend the change touches. Find the tests that cover them:\n\n       grep -rliE \"elegoo_neptune_3_tft|dwin_lcd_proui|touch_ui_ftdi_eve|extui_example|dgus_lcd_ui\" buildroot/tests\n\n   Build one with `buildroot/bin/mftest -t <env> -n <N> -y`.\n\n2. Backends with no CI test need a manual build. Use an STM32 board, not AVR RAMPS, because serial LCDs on RAMPS fail on a pin conflict with Serial Port 3:\n\n       buildroot/bin/restore_configs\n       buildroot/bin/opt_set MOTHERBOARD BOARD_BTT_SKR_PRO_V1_1 SERIAL_PORT -1 LCD_SERIAL_PORT 1\n       buildroot/bin/opt_enable EEPROM_SETTINGS SDSUPPORT AUTO_BED_LEVELING_BILINEAR FIX_MOUNTED_PROBE Z_SAFE_HOMING PIDTEMPBED\n       buildroot/bin/opt_enable MALYAN_LCD            # or the backend's own option(s)\n       buildroot/bin/mftest -a -n1 -y\n\n   Some backends need extra options (for example `LCD_BED_TRAMMING` and `BABYSTEPPING` for Anycubic, `CLASSIC_JERK` for DGUS E3S1PRO). Read the first error.\n\n3. Judge each build by its log: count `[SUCCESS]` and `error:`. Don't pass `-s` to `mftest`, which hides the log. Run builds one at a time in a single clone, since parallel builds collide on the `.pio` state.\n4. Confirm an override really happened. With the ARM toolchain's `nm`:\n\n       arm-none-eabi-nm -C .pio/build/<env>/firmware.elf | grep -E ' ExtUI::on[A-Za-z]+\\('\n\n   `T` is the backend's own definition, and `W` is the weak default. A handler you defined that still shows `W` wasn't compiled, so check the guards around it.\n\n5. Run `buildroot/bin/restore_configs` between builds and at the end. `git status --short` should then show only your source edits.\n6. For a serial-panel backend, also run it end to end in the simulator with a scripted panel emulator. Check that replies are valid single lines, that the panel is still answered while Marlin is busy, and that each translated command takes effect.\n","frontmatter":{"name":"marlin-extui-backends","description":"Use when adding or changing an ExtUI display backend (DGUS, Anycubic, Nextion, Malyan, FTDI EVE, Neptune 3, IA Creality, serial panels) or the ExtUI API in ui_api.h / ui_api.cpp."},"isInternal":false,"tokens":1562,"sizeBytes":5868},{"name":"SKILL.md","path":"buildroot/skills/marlin-fork-harvesting/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-fork-harvesting/SKILL.md","title":"marlin-fork-harvesting","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-fork-harvesting\ndescription: Use when evaluating a GitHub fork of Marlin (a vendor's or a community member's) and porting a feature, board, or display driver into mainline as a clean, reviewable PR.\n---\n\n# Port a Feature from a Marlin Fork\n\nForks often hold support for hardware that mainline lacks. Their code can't be merged as-is: it is based on an older Marlin, may restructure the HALs, and may not follow the project style. Port the part that belongs in mainline, and port it cleanly.\n\nCheck the fork's license and keep attribution. Say in the PR where the code came from and credit the author.\n\n## 1. Decide whether it is worth porting\n\n| Question | How to check |\n| --- | --- |\n| Is it maintained? | Last push date |\n| Is it really Marlin? | It has `Marlin/src/` |\n| Is it already in mainline? | Search the Marlin PRs and `git log` for the board or feature |\n| Does it add hardware or features mainline lacks? | See step 3 |\n| Is the quality reasonable? | Read two or three key files |\n\nThe vendor's own repo is a secondary source. It is often a frozen snapshot from the product launch, based on an old Marlin, with stale board IDs and an outdated layout. Use it only to cross-check pin assignments and names, not as a port base.\n\n## 2. Add the fork as a remote\n\n    git remote add <author-slug> https://github.com/<Author>/Marlin\n    git fetch <author-slug>\n    git branch -r | grep <author-slug>\n\n## 3. Find only the relevant changes\n\nA fork that diverged long ago shows thousands of changed files, nearly all of it mainline drift. Filter to what matters:\n\n    git log --oneline upstream/bugfix-2.1.x..<slug>/<branch> | wc -l\n    git diff --name-only upstream/bugfix-2.1.x <slug>/<branch> | grep -iE '<machine>|src/pins/|src/HAL/|src/lcd/'\n    git show <slug>/<branch>:Marlin/Configuration.h | grep -E 'MOTHERBOARD|MACHINE_NAME'\n\nCheck each of these:\n\n- **New HAL?** If the board needs an MCU family that mainline doesn't support, decide with the maintainers whether the HAL is part of this PR or a separate one before you write code.\n- **Board entry?** Check `Marlin/src/core/boards.h`. A new board needs a `boards.h` entry, a `pins.h` entry, and a pins file.\n- **UI code?** Large display-driver or UI changes should go in a separate PR from the core feature.\n\n## 4. Port, don't cherry-pick\n\nMake a clean-room port in most cases. Read the fork's code, then write it in mainline's style on a fresh branch:\n\n    git checkout -b <topic> upstream/bugfix-2.1.x\n\nCherry-pick only when the commits are linear, clean, and conflict-free.\n\nNever port a fork's HAL restructuring (deleted platforms, renamed folders, moved files). Take the target board's pins file and the genuinely new driver or feature.\n\n### Rules\n\n1. **Use mainline's names and layout.** A fork's folder names may differ (for example `hc32f46x/` vs mainline `hc32f4/`). Check `ls Marlin/src/pins/` and follow it.\n2. **Board IDs must be verified against `boards.h`.** A fork's ID is often already taken in mainline. Use the next free ID in the right architecture range:\n\n       grep '#define BOARD_' Marlin/src/core/boards.h | awk '{print $3}' | sort -n\n\n   Keep the `boards.h` order the same as the `#elif MB()` order in `pins.h`.\n\n3. **Config defaults** go in the `Conditionals-*.h` layer. `SanityCheck.h` only validates currently-valid combinations.\n4. **Renamed or removed options** get an `#error` in `Marlin/src/inc/Changes.h` that points to the replacement.\n5. **Old G-codes and M-codes** that the port replaces should keep working as shims where reasonable.\n6. **EEPROM data** follows the struct pattern in `settings.cpp`, with a version bump.\n7. **Don't copy old APIs.** Fork code often uses names removed from mainline. Fix each compile error against current headers rather than adding compatibility macros.\n\n### New ExtUI display drivers\n\nSee skill marlin-extui-backends. In particular:\n\n- Add the driver folder to `ini/features.ini`. If it is missing, the whole driver is silently excluded and every ExtUI callback is \"undefined\".\n- Add the new LCD define to the lists in `Conditionals-2-LCD.h`, and add its `SanityCheck.h` entries.\n- Compare the fork's handlers with the current `ui_api.h` (renamed `onMediaMounted`, handlers with new argument types, newer handlers such as `onSurviveInKilled`).\n\n### Per-HAL notes\n\nRead `Marlin/src/HAL/<FAMILY>/AGENTS.md`. For example, a HAL may lack an EEPROM backend the fork relied on (`PersistentStore::*` undefined at link), or need a board-defined crystal frequency.\n\n## 5. Build and test\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_<NAME>\n    buildroot/bin/mftest -a -n1 -y\n    buildroot/bin/restore_configs\n\n- Build with the feature disabled and enabled.\n- Add a test config for new options (skill: marlin-build-test). A test config must satisfy the sanity checks, for example `NOZZLE_AS_PROBE` needs a zero probe offset, and bed leveling with a physical probe needs `Z_SAFE_HOMING`.\n- Say honestly in the PR what you built, and that no one has tested on hardware if that is so.\n\n## 6. Commit and PR\n\nSplit it: core pins and board first, display driver second, extra features after that (skill: marlin-pr-workflow). Credit the fork's author in the PR description.\n","frontmatter":{"name":"marlin-fork-harvesting","description":"Use when evaluating a GitHub fork of Marlin (a vendor's or a community member's) and porting a feature, board, or display driver into mainline as a clean, reviewable PR."},"isInternal":false,"tokens":1335,"sizeBytes":5206},{"name":"SKILL.md","path":"buildroot/skills/marlin-hal-work/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-hal-work/SKILL.md","title":"marlin-hal-work","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-hal-work\ndescription: Use when adding a board to an existing HAL, extending a HAL, creating a new HAL, or debugging HAL-specific code. Includes the board checklist, comparing HALs, and verifying framework APIs.\n---\n\n# Marlin HAL and Board Work\n\nEach HAL folder `Marlin/src/HAL/<FAMILY>/` has its own `AGENTS.md` with the build and test loop and the gotchas for that family. Read it first.\n\nThree levels of work, from most to least common:\n\n1. **Add a board** to an existing HAL.\n2. **Extend a HAL** with an MCU variant.\n3. **Create a new HAL** for a new architecture.\n\n## Compare, don't generalize\n\nDon't assume one HAL's behavior holds for another. Timers, ISR entry, serial buffering, and ADC differ a lot. To learn the contract, compare three representatives: a simple one (AVR), a mid one (LPC1768), and a complex one (STM32 or ESP32).\n\n    ls Marlin/src/HAL/*/                      # a file in 10+ HALs is probably required\n    head -80 Marlin/src/HAL/{AVR,LPC1768,STM32,RP2040}/timers.h\n    head -50 Marlin/src/HAL/{AVR,LPC1768,STM32,RP2040}/fastio.h\n\n`Marlin/src/HAL/shared/` holds what every HAL inherits (`Marduino.h`, `serial_ports.h`, `eeprom_api.h`, ...). `Marlin/src/HAL/platforms.h` is the single dispatch point: a wrong detection macro loads the wrong HAL, and the failure is confusing.\n\n## Adding a board\n\n| Step | File | Action |\n| --- | --- | --- |\n| Pins | `Marlin/src/pins/<arch>/pins_<BOARD>.h` | All `*_PIN` defines. Use a `_common.h` file when variants share a pinout |\n| Board ID | `Marlin/src/core/boards.h` | `#define BOARD_<NAME> <ID>` in the right architecture range, highest ID + 1 |\n| Routing | `Marlin/src/pins/pins.h` | `#elif MB(<NAME>)` in the same relative position as in `boards.h` |\n| Environment | `ini/<arch>.ini` | Add an `[env:...]` that `extends` a similar one |\n| Validation | `Marlin/src/pins/<arch>/env_validate.h` | Only if the family has a unique constraint |\n| CI test | `buildroot/tests/<env>/` | Optional |\n\nDo not edit `config.ini` for a board.\n\nConventions:\n\n- Letters between `pins_` and `.h` are UPPERCASE. The `_common` suffix is lowercase. Prefix the manufacturer's name if the board name isn't distinctive.\n- The `#define` order in `boards.h` must match the `#elif MB()` order in `pins.h`.\n- Set `BOARD_INFO_NAME` (and optionally `DEFAULT_MACHINE_NAME`) in the board's pins file.\n- Use `#ifndef` guards around pins that variants may override.\n- The EEPROM backend is chosen per board: most boards define `FLASH_EEPROM_EMULATION` or `SDCARD_EEPROM_EMULATION` in the pins file. If none is enabled you get an `undefined reference to PersistentStore::*` at link.\n- Link the schematic or source of the pin data in a comment, if there is one.\n\n### Verifying pin and UART assignments\n\nCheck open sources before saying a pin can't be verified without hardware. Cross-check at least two:\n\n1. Klipper configs (`Klipper3d/klipper/config/printer-<vendor>-<model>*.cfg`, `generic-<board>.cfg`). They list step/dir/enable/heater/fan pins, and the MCU serial pins in comments.\n2. Vendor or community Marlin forks: `pins_*.h`, the variant's `variant.h` and `PeripheralPins.c`, and the ini env's `build_flags`.\n3. Elimination: a UART has only a few alternate-function pin pairs. If the others are used by steppers or heaters, the one left is the answer.\n\nSTM32duino uses `PIN_SERIALn_RX/TX` if defined. Otherwise it takes the first matching entry in the variant's pin map. A board whose UART is on a later pair needs `-DPIN_SERIALn_RX=...` and `-DPIN_SERIALn_TX=...` in its env.\n\n### Validate with mftest\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_<NAME>\n    buildroot/bin/mftest -a -n1 -y\n\nRaw `pio run -e <env>` reads whatever `Configuration.h` was last generated, and can report a phantom `Build environment is incompatible with BOARD_...`. Trust `mftest` and don't chase a pio-only \"incompatible\" error. A passing build only shows the files are structurally sound. Say so in the PR if no one has tested the board.\n\n## Creating a new HAL\n\nThe minimum set that must exist to compile:\n\n- A branch in `platforms.h`\n- `HAL.h` and `HAL.cpp` (the `MarlinHAL` class)\n- `fastio.h`\n- `timers.h` and `timers.cpp`\n- `MarlinSerial.h` and `MarlinSerial.cpp`\n- `inc/Conditionals-2-LCD.h` and `inc/SanityCheck.h`, plus the other files in `inc/`, which can be empty (`#pragma once`) but must exist\n\nUse a recent HAL such as HC32 as the layout reference. Everything else fills in as features are enabled.\n\n## Verify against the real framework\n\nCode written without the vendor framework in view tends to invent names borrowed from another platform (for example `GPIO_TypeDef`, `BSRR`, `Pin_TypeDef`). The installed framework (`~/.platformio/packages/framework-*`) is ground truth: grep it before writing or \"fixing\" any peripheral symbol. A symbol that isn't there is a HAL bug, not a missing dependency.\n\n## Pitfalls\n\n- A HAL header missing `#include \"../../inc/MarlinConfig.h\"` makes shared files report `FSTR_P`, `PGM_P`, `millis_t`, or `pin_t` undefined. Fix the one include, not the cascade. Files in `HAL/<ARCH>/<sub>/` need `../../../inc/MarlinConfig.h`.\n- A timer ISR macro with no vector or NVIC wiring compiles but never fires.\n- `MF_TIMER_STEP`, `MF_TIMER_TEMP`, and `MF_TIMER_PWM` must not collide with each other or with platform-reserved timers.\n- `CRITICAL_SECTION_START/END` must nest (save and restore the previous interrupt state). Don't use bare `__disable_irq()` / `__enable_irq()`.\n- `pin_t` must match the platform's GPIO numbering.\n- `SERIAL_PORT` numbering differs between HALs (AVR 0-3, STM32 and RP2040 -1/0/1).\n- A serial adapter class must subclass the real core serial class. Forward-declaring it gives \"incomplete type\" errors.\n- Some cores lack the bare `Serial` global or `spiBegin()`, which Marlin code calls.\n- Vendored framework files may use CRLF. Some editors and tools turn an escape such as `\"\\r\\n\"` into a real control character, so use hex constants like `0x0D`.\n- Don't remove or break the AVR HAL. It is the reference implementation.\n- Some boards must keep the base AVR flash and RAM budget. Don't let the base build grow.\n","frontmatter":{"name":"marlin-hal-work","description":"Use when adding a board to an existing HAL, extending a HAL, creating a new HAL, or debugging HAL-specific code. Includes the board checklist, comparing HALs, and verifying framework APIs."},"isInternal":false,"tokens":1621,"sizeBytes":6111},{"name":"SKILL.md","path":"buildroot/skills/marlin-pr-workflow/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-pr-workflow/SKILL.md","title":"marlin-pr-workflow","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-pr-workflow\ndescription: Use when preparing, splitting, or submitting a Marlin Pull Request. Covers PR size, branch, commit messages, rebase, and the PR template.\n---\n\n# Marlin Pull Request Workflow\n\nPolicy background: `.github/contributing.md` (AI-Assisted Contributions) and `AGENTS.md` section 11.\n\n## 1. Keep it small\n\nA PR does one thing and builds on its own. If your working tree holds more than one logical change, or a reviewer would struggle to follow it, split it into two or more PRs:\n\n- refactor first, behavior change second\n- core logic first, LCD/UI support second\n- one HAL, board family, or pin file group per PR\n- unrelated fixes in their own PRs\n\nTo split: create a new branch from `upstream/bugfix-2.1.x` for each piece and bring over only its changes (`git checkout <work-branch> -- <paths>` or `git cherry-pick -n`, then commit selectively with `git add -p`). Build each branch separately. State any dependency between the PRs in their descriptions.\n\n## 2. Branch\n\n    git fetch upstream\n    git checkout -b my_change upstream/bugfix-2.1.x\n\nPRs target `bugfix-2.1.x`, never a release branch.\n\n## 3. Before committing\n\n- Format only your changed files (skill: marlin-code-style).\n- Build the affected environments (skill: marlin-build-test).\n- Restore the config files after testing (`buildroot/bin/restore_configs` discards uncommitted edits to them, so commit any real config changes first). Don't commit test-only config edits.\n- Add tests for any new feature or option: a `buildroot/tests/<env>/config-NN.ini` build test, and a unit test under `Marlin/tests` when the logic can run without hardware (skill: marlin-build-test).\n- Review `git diff` for unrelated changes.\n\n## 4. Commit\n\n- Imperative mood, 72 characters or fewer on the first line, gitmoji-style emoji prefix like recent `git log` entries (🐛 fix, ✨ feature, ⚡️ performance, 🔧 config, 🩹 minor fix, 🧑‍💻 developer, 🚸 UX).\n- One logical change per commit. Squash fixups.\n\n## 5. Keep current\n\nRebase, don't merge:\n\n    git fetch upstream\n    git rebase upstream/bugfix-2.1.x\n    git push -f\n\n## 6. Open the PR\n\nFill in every section of `.github/pull_request_template.md`. No issue numbers in the title. List what you built and tested, and on what hardware if any. Attach a Configurations ZIP if reviewers need one to test.\n\nThe `mf*` helper scripts (`mfnew`, `mfrb`, `mfpr`, ...) described at https://marlinfw.org/docs/development/git_scripts.html are optional conveniences for these steps.\n","frontmatter":{"name":"marlin-pr-workflow","description":"Use when preparing, splitting, or submitting a Marlin Pull Request. Covers PR size, branch, commit messages, rebase, and the PR template."},"isInternal":false,"tokens":642,"sizeBytes":2519},{"name":"SKILL.md","path":"buildroot/skills/marlin-solving-bugs/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-solving-bugs/SKILL.md","title":"marlin-solving-bugs","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-solving-bugs\ndescription: Use when investigating and fixing a Marlin bug report, from reproducing it to a verified patch. Covers triage, finding the code path, isolating the cause, a minimal root-cause fix, and verification.\n---\n\n# Fix a Marlin Bug\n\nFind the root cause before you write any fix. Marlin has many HALs and a huge number of configuration combinations, and there is no hardware-in-the-loop CI, so the evidence you gather matters more than the size of the patch.\n\n## 1. Triage the report\n\n- Get the exact Marlin version or commit, the board, and `Configuration.h` / `Configuration_adv.h` (a `marlin_config.json` or a Configurations ZIP is best).\n- Find the first version that failed, and the last that worked. If the reporter can test, ask for a bisect (`git bisect`) between those points.\n- Check whether `bugfix-2.1.x` still has the bug. Fixes land there first, and old reports are often already fixed.\n- Files move between releases. Search for the current location of any path in an old report: `ultralcd.*` became `marlinui.*`, and `Conditionals_post.h` became `Conditionals-5-post.h`.\n\n## 2. Find the code path\n\n    git grep -n \"OPTION_NAME\" -- Marlin/src            # who uses an option\n    git log --oneline -20 -- Marlin/src/module/motion.cpp\n    git log -S\"suspicious_string\" --oneline -- Marlin/src\n    git log --oneline 2.1.2..2.1.3 -- Marlin/src/module/probe.cpp\n\n- G-code handlers are in `Marlin/src/gcode/`, with one file per command or small group.\n- Trace which `#if` paths the reporter's config enables. A bug often appears only for one combination of options.\n- A regression usually has a specific commit. Read its diff and its PR discussion to learn what it was meant to do.\n\n## 3. Isolate the cause\n\n- Reproduce on the smallest config you can, using `buildroot/bin/opt_*` to set only the needed options (skill: marlin-config-options).\n- Test as much as possible in the native simulator ([MarlinSimUI](https://github.com/MarlinFirmware/MarlinSimUI)) or a unit test, so you can run and re-run the scenario.\n- Change one thing at a time and keep notes. A temporary `SERIAL_ECHOLNPGM` can help, but remove every debug statement before committing.\n- State your hypothesis and the evidence before coding the fix. If you can, show that reverting the suspect change removes the symptom.\n\n## 4. Fix the root cause\n\n- Fix the wrong logic itself. Don't add workarounds elsewhere that hide the symptom.\n- Make the smallest diff that does it. No reformatting, no unrelated cleanup (skill: marlin-code-style).\n- Think about every configuration the changed code is compiled in. Build with the relevant options both on and off.\n\nCommon causes:\n\n| Symptom | Look for |\n| --- | --- |\n| Wrong distance or speed | mm vs steps vs mm/s units; missing conversion |\n| Works with one option, breaks with another | A missing `ENABLED()` guard, or the wrong layer in `Conditionals-*.h` |\n| Wrong axis | `X_AXIS` used where `Z_AXIS` was meant; macro for the wrong axis |\n| Fails only on AVR | Integer overflow (use `uint32_t`), `double`, RAM and flash limits |\n| Fails only on one board | HAL differences: timers, serial buffering, pin multiplexing |\n| Position drift after an event | A reset of `current_position` or the planner position without `sync_plan_position()` |\n| Lost settings after upgrade | A `settings.cpp` layout change without a version bump |\n\n## 5. Verify\n\n- Build with `mftest` (skill: marlin-build-test). At least one AVR (tight flash) and one 32-bit environment for common code.\n- Run the reporter's scenario again, not just a build. A passing build is not proof the bug is fixed.\n- If the logic is testable without hardware, add a unit test that fails before your fix and passes after.\n- If you add a config interaction, add a check to `Marlin/src/inc/SanityCheck.h`.\n\n## 6. Submit\n\nFollow skill marlin-pr-workflow. In the PR description, say what the root cause was, how you reproduced it, and what you tested (including on what hardware, if any). Reference the issue in the description, not the title. Don't claim the issue is closed until the reporter or another tester confirms it on hardware.\n\n## Pitfalls\n\n- **Debug code left in.** `#define DEBUG_OUT 1`, `DEBUG_ECHOLNPGM`, and temporary prints must not be in the final diff. Check with `git diff | grep -n DEBUG_`.\n- **Stale `case` labels.** After an enum value is removed, a `case REMOVED_VALUE:` can survive a rebase. Grep for removed names when a PR touches enums.\n- **Shared string buffers.** The `numtostr` family (`ftostr*`, `i16tostr*`) writes into one shared buffer. Never pass one function's result into another, and don't hold a pointer across calls.\n- **Variables declared outside the guard of their only uses** cause `-Wunused-variable`, and CI treats warnings as errors. Put the declaration inside the same guard.\n- **A function whose signature uses a type from a guarded header** needs its definition guarded the same way as its declaration.\n- **Verify a premise before relying on it.** For example, `IS_SCARA` also covers TPARA, so grep the macro definition before saying a path doesn't apply to a variant.\n\n## Reading a failed CI run\n\n    gh run view <run_id> --repo MarlinFirmware/Marlin --json jobs -q '.jobs[] | select(.conclusion==\"failure\") | [.databaseId, .name] | @tsv'\n    gh run view <run_id> --job <job_id> --log | grep -E \"error:|FAILED|undefined\" | head -30\n\nA job that failed in under 30 seconds is nearly always a compile or sanity-check error. Longer ones are often timeouts.\n","frontmatter":{"name":"marlin-solving-bugs","description":"Use when investigating and fixing a Marlin bug report, from reproducing it to a verified patch. Covers triage, finding the code path, isolating the cause, a minimal root-cause fix, and verification."},"isInternal":false,"tokens":1371,"sizeBytes":5507},{"name":"SKILL.md","path":"buildroot/skills/marlin-unit-tests/SKILL.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/marlin-unit-tests/SKILL.md","title":"marlin-unit-tests","category":"anthropic-skill","format":"markdown","content":"---\nname: marlin-unit-tests\ndescription: Use when writing or running Marlin's native unit tests (make unit-test-*), or when a native unit-test build fails locally, especially on macOS.\n---\n\n# Marlin Native Unit Tests\n\nBackground: `test/README.md`. For adding a test to a feature PR, see also the unit-test section of skill marlin-build-test.\n\n## Layout\n\n- Test sources: `Marlin/tests/<area>/test_<name>.cpp` (Unity, `MARLIN_TEST(group, name)`).\n- Test configurations: `test/NNN-name.ini`. Each one builds a separate test binary with a different Marlin configuration. Suites are discovered from this folder by `buildroot/share/PlatformIO/scripts/collect-code-tests.py`.\n- The PlatformIO environment is `linux_native_test` in `ini/native.ini`. It is built on the Linux native HAL, with `-Werror`.\n\n## Run\n\n    make unit-test-all-local                              # all configs\n    make unit-test-single-local UNIT_TEST_CONFIG=default  # one config (test/001-default.ini)\n    make unit-test-all-local-docker                       # same, in a container\n\n## Write one\n\n1. Copy the license header and macros from an existing test, such as `Marlin/tests/feature/test_runout.cpp`.\n2. Wrap the file in `#if ENABLED(FEATURE)` so it compiles only for configs that enable the feature.\n3. If no `test/*.ini` enables the feature, add one with `motherboard = BOARD_SIMULATED` and only the options required to pass the sanity checks.\n4. Test logic, not hardware: parsing, math, conversions, state machines. Anything needing real pins or timers isn't a good candidate.\n\n## macOS\n\nOn macOS `/usr/bin/gcc` is Clang, which cannot build Marlin. Install GCC with MacPorts or Homebrew, then:\n\n    buildroot/bin/mac_gcc macports      # or: homebrew   (apple resets to the system compiler)\n    which gcc                           # should now be the real GCC\n\nIf the native build fails on macOS, first check the compiler diagnostics and which `gcc` you got. Don't assume the failure is a bug in the test wiring.\n\n## Pitfalls\n\n- Test files are compiled with `-Werror`. An unused variable or sign-compare warning fails the run.\n- Don't work around a compiler warning with duplicate includes or duplicate keys in the INI.\n- Run the test configs one at a time. They share the `.pio` build directory.\n","frontmatter":{"name":"marlin-unit-tests","description":"Use when writing or running Marlin's native unit tests (make unit-test-*), or when a native unit-test build fails locally, especially on macOS."},"isInternal":false,"tokens":526,"sizeBytes":2273},{"name":"README.md","path":"buildroot/skills/README.md","rawUrl":"https://raw.githubusercontent.com/MarlinFirmware/Marlin/HEAD/buildroot/skills/README.md","title":"Skills Skill","category":"anthropic-skill","format":"markdown","content":"# Marlin Contributor Skills\n\nStep-by-step procedures for working on Marlin, written for AI coding agents and equally useful as human cheat sheets. Each skill is a folder containing a `SKILL.md` with YAML front matter (`name`, `description`) followed by instructions. They refer to the scripts in `buildroot/bin` and the `Makefile` so the procedures stay accurate as the scripts evolve.\n\nAll commands assume the current directory is the repository root.\n\n| Skill | Purpose |\n| --- | --- |\n| [marlin-build-test](marlin-build-test/SKILL.md) | Build a board environment, apply test configs, run CI-style tests |\n| [marlin-config-options](marlin-config-options/SKILL.md) | Find, enable, disable, and set configuration options from the command line |\n| [marlin-code-style](marlin-code-style/SKILL.md) | Follow the Marlin coding standards and indentation; leave existing code unformatted |\n| [marlin-pr-workflow](marlin-pr-workflow/SKILL.md) | Prepare a focused, reviewable Pull Request |\n| [marlin-solving-bugs](marlin-solving-bugs/SKILL.md) | Investigate, isolate, fix, and verify a bug report |\n| [marlin-extui-backends](marlin-extui-backends/SKILL.md) | Add or change ExtUI display backends and the ExtUI API |\n| [marlin-unit-tests](marlin-unit-tests/SKILL.md) | Write and run native unit tests; macOS GCC setup |\n| [marlin-codebase-navigation](marlin-codebase-navigation/SKILL.md) | Find definitions, uses, and flows in the source with git and grep |\n| [marlin-hal-work](marlin-hal-work/SKILL.md) | Add a board, extend a HAL, or create a new HAL |\n| [marlin-fork-harvesting](marlin-fork-harvesting/SKILL.md) | Evaluate a Marlin fork and port its features cleanly |\n\n## Adding a skill\n\n- Make a folder `buildroot/skills/<skill-name>/` with a `SKILL.md`.\n- Start `description` with when to use the skill. Keep the skill short and procedural.\n- Refer to scripts in `buildroot/bin` instead of duplicating their logic, and verify every command you document.\n- Skills are for all contributors. Don't include personal paths or maintainer-only procedures.\n- Add the skill to the table above.\n","isInternal":false,"tokens":520,"sizeBytes":2082}],"systemPromptSnippet":"<yakaai_skills repo=\"MarlinFirmware/Marlin\">\n<!-- File: AGENTS.md (Tokens: ~8685 | Category: root-instruction) -->\n# Marlin 3D Printer Firmware\n\n> This file consolidates the project summary (`docs/project-summary.md`) into the top-level agent reference. It is the canonical orientation document for the whole repo. Per-HAL working notes live in `Marlin/src/HAL/<FAMILY>/AGENTS.md` (e.g. `Marlin/src/HAL/AT32/AGENTS.md`).\n\n## 1. Executive Summary\n\nMarlin is an open-source firmware for 3D printers, written in C/C++ and built on the Arduino framework via PlatformIO. It sits between a host computer (running slicer software) and the printer's hardware, translating G-code movement commands into precise stepper motor control, temperature regulation, and peripheral management. The firmware supports 15+ microcontroller platforms (STM32, AVR, ESP32, RP2040, and more), 400+ pin configuration files across 24 board families, and a modular feature system with 80+ enable/disable features. At over 2,500 source files, Marlin is one of the most widely deployed 3D printer firmware projects in the world, licensed under GPL v3.0.\n\n## 2. Architecture Overview\n\nMarlin follows a layered embedded firmware architecture organized around a Hardware Abstraction Layer (HAL), a G-code command processing pipeline, a set of core motion/thermal modules, and a rich feature system. The firmware's singleton entry point is the `Marlin` class (`MarlinCore`), which manages global state and the main control loop.\n\n```mermaid\ngraph TB\n    subgraph Environment[\"Printer Environment\"]\n        Host[\"Host Computer\\n(Slicer / Host Software)\"]\n        SD[\"SD Card\\n(SdFat)\"]\n        Sensors[\"Thermistors &\\nEndstops\"]\n        Motors[\"Stepper Motors\\n&amp; Drivers\"]\n        Heaters[\"Heaters &amp; Fans\"]\n        Display[\"LCD / TFT / DWIN\"]\n    end\n\n    subgraph Marlin[\"Marlin Firmware\"]\n        subgraph GCode[\"G-Code Subsystem\"]\n            Queue[\"GCodeQueue\\nRing Buffer\"]\n            Parser[\"GCodeParser\\nParameter Extraction\"]\n            Suite[\"GcodeSuite\\nCommand Dispatcher\"]\n        end\n        subgraph Modules[\"Core Modules\"]\n            Motion[\"Motion Planner\"]\n            Planner[\"Block Planner\"]\n            Stepper[\"Stepper Driver\"]\n            Temp[\"Temperature Manager\"]\n        end\n        subgraph Features[\"Feature System\\n80+ modules\"]\n            BedLevel[\"Bed Leveling\"]\n            PowerLoss[\"Power Loss Recovery\"]\n            Runout[\"Filament Runout\"]\n            MMU[\"Multi-Material\\nUnit\"]\n        end\n        subgraph HAL[\"Hardware Abstraction Layer\"]\n            Shared[\"HAL Shared Code\"]\n            Platforms[\"15+ Platform HALs\"]\n        end\n    end\n\n    Host -->|\"USB Serial\\n250k baud\"| Suite\n    SD -->|\"SD Interface\"| Queue\n    Suite --> Parser\n    Parser --> Suite\n    Queue --> Suite\n    Suite --> Motion\n    Suite --> Temp\n    Motion --> Planner\n    Planner --> Stepper\n    Stepper --> Motors\n    Temp --> Heaters\n    Temp --> Sensors\n    Display <-->|\"SPI/I2C/UART\"| Suite\n```\n\nThe firmware communicates with the host via serial at 250,000 baud (configurable), receiving G-code commands line-by-line. Commands are queued in a circular buffer (`GCodeQueue::ring_buffer`), parsed by `GCodeParser`, and dispatched by `GcodeSuite` to category-specific handlers (motion, temperature, configuration, calibration, etc.). The core modules handle the low-level real-time tasks: the planner generates motion blocks with jerk control and acceleration profiling, the stepper driver issues pulse trains, and the temperature manager runs PID loops on thermistor inputs.\n\n### Supported Hardware Platforms\n\n| Platform Family | MCUs | Architecture | Notes |\n| --- | --- | --- | --- |\n| STM32 (various) | STM32F0, F1, F4, F7, H7, G0 | ARM Cortex-M | Primary 32-bit target |\n| AVR | ATmega2560, ATmega1280, AT90USB | 8-bit AVR | Legacy support |\n| ESP32 | ESP32-S3, ESP32-S2 | Xtensa/RISC-V | WiFi/BLE capability |\n| SAMD | SAMD21, SAMD51 | ARM Cortex-M0+/M4 | Adafruit Feather M4 |\n| RP2040 | RP2040, RP2350 | ARM Cortex-M33 | Raspberry Pi Pico |\n| LPC | LPC1768, LPC1769 | ARM Cortex-M3 | RAMBo, Melzi boards |\n| GD32 | GD32F1, GD32F3 | ARM Cortex-M3 | GigaDevice clones |\n| HC32 | HC32F4 | ARM Cortex-M4 | JCEC chips |\n| AT32 | AT32F4 | ARM Cortex-M4 | Artery Tek |\n| Teensy | 3.1/3.2, 3.5/3.6, 4.0/4.1 | ARM Cortex-M4/M7 | PJRC boards |\n| Linux Simulator | x86/x64 | Native | CI/testing |\n| Native Simulator | Any | Native | Unit testing |\n\n## 3. Processing Pipeline\n\nThe G-code processing pipeline follows a well-defined flow from input ingestion through parsing, dispatch, and hardware actuation.\n\n```mermaid\nflowchart TD\n    InputSerial[\"Serial Input\\nUSB / UART\"] --> Queue\n    InputSD[\"SD Card\\nSdFat\"] --> Queue\n    InputProgmem[\"Program Memory\\nPROGMEM\"] --> Queue\n\n    Queue[\"GCodeQueue\\nRing Buffer\\nBUFSIZE entries\"] --> Parser\n\n    Parser[\"GCodeParser\\n• Letter/Code/Subcode\\n• Parameter extraction\\n• FASTER_GCODE_PARSER\\n  pre-scanned flags\"] --> Suite\n\n    Suite[\"GcodeSuite\\nCommand Dispatcher\\n• Subcommand routing\\n• Parameter validation\\n• Endstop events\"]\n\n    Suite --> CatMotion[\"Motion\\nG0/G1/G2/G3\"]\n    Suite --> CatTemp[\"Temperature\\nM104/M105/M106/M107/M109\"]\n    Suite --> CatConfig[\"Configuration\\nM200-M205/M301/M92\"]\n    Suite --> CatCalibrate[\"Calibration\\nG28/G33/G34/G425\"]\n    Suite --> CatSD[\"SD Card\\nM20-M34/M928\"]\n\n    CatMotion --> Planner\n    CatTemp --> Temp\n    CatCalibrate --> Motion\n\n    Planner[\"Block Planner\\n• Trapezoidal profile\\n• Jerk control\\n• Buffer: BLOCK_BUFFER_SIZE\"] --> Stepper\n    Motion[\"Motion\\n• axis_position()\\n• relative_mode\\n• dual_x_carriage\"] --> Planner\n    Temp[\"Temperature\\n• PID loops\\n• thermistor tables\\n• safety checks\"] --> Heaters\n\n    Stepper[\"Stepper Driver\\n• issue_pending()\\n• TMC SPI config\\n• Trinamic drivers\"] --> Output[\"Hardware Outputs\\n• Step/Dir pulses\\n• PWM heat/fan\\n• Serial responses\"]\n```\n\n### Key Pipeline Components\n\n**GCodeQueue** — A circular ring buffer (`RingBuffer`) that holds up to `BUFSIZE` G-code command strings. Commands enter via three injectors: serial input (USB/UART), SD card file reading, and in-firmware injected commands (PROGMEM). The queue's `advance()` method pops the next command and hands it to the parser.\n\n**GCodeParser** — Parses a single G-code line, extracting the command letter (G/M/T), code number, subcode, and all parameter values (X, Y, Z, E, F, S, P, etc.). When `FASTER_GCODE_PARSER` is enabled, the parser pre-scans all parameters into a flags array for O(1) lookups.\n\n**GcodeSuite** — The command dispatcher singleton. Each G/M code maps to a handler method within this class. Commands are routed to category subdirectories under `src/gcode/` (e.g., `motion/`, `temp/`, `config/`, `calibrate/`). The suite also handles pre- and post-command hooks for endstop events, buffer monitoring, and inactivity shutdown.\n\n## 4. Core Components\n\n### 4.1 Firmware Entry Point\n\nThe firmware lifecycle is managed by `MarlinCore.cpp` (~1,777 lines), structured around Arduino's `setup()` and `loop()` functions:\n\n- **`setup()`** — Initializes HAL pins, serial ports, all modules (stepper, temperature, motion, planner, settings), features (runout detection, power loss recovery, LEDs), and the UI subsystem. Each initialization block is wrapped in `SETUP_RUN()` for debug logging in dev mode.\n- **`loop()`** — Runs continuously (infinite loop on AVR, structured loop on 32-bit platforms). Each iteration calls `marlin.idle()`, processes the G-code queue, checks power-off timers, and handles endstop events.\n\n### 4.2 Singleton Architecture\n\nGlobal state is managed through singletons:\n\n| Singleton | File | Responsibility |\n| --- | --- | --- |\n| `marlin` | `MarlinCore.cpp/h` | Global state machine, inactivity management, kill/suicide, heatup waits |\n| `gcode` | `gcode/gcode.cpp` | G-code command dispatch |\n| `queue` | `gcode/queue.h` | Command queue ring buffer |\n| `planner` | `module/planner.cpp` | Motion block buffer and trapezoidal profiling |\n| `stepper` | `module/stepper.cpp` | Step pulse generation and TMC driver config |\n| `thermalManager` | `module/temperature.cpp` | PID loops, thermistor reading, safety monitoring |\n| `card` | `sd/cardreader.cpp` | SD card file system operations |\n| `ui` | `lcd/marlinui.cpp/h` | LCD/UI state machine and menu system |\n\n### 4.3 Module System\n\nThe `module/` directory contains the core motion and thermal management classes:\n\n| Module | File | Description |\n| --- | --- | --- |\n| **Motion** | `motion.h/cpp` | High-level axis positioning, coordinate transforms, joint interpolation |\n| **Planner** | `planner.h/cpp` | Block buffer, trapezoidal velocity profiling, junction deviation |\n| **Stepper** | `stepper.h/cpp` | Step pulse generation, direction control, TMC Trinamic SPI configuration |\n| **Temperature** | `temperature.h/cpp` | PID autotune (M303), thermistor tables, hotend/bed/heater management |\n| **Endstops** | `endstops.h/cpp` | Endstop interrupt handling, homing logic, soft endstops |\n| **Probe** | `probe.h/cpp` | Bed probing (G30, G31/G32, G38, bltouch), Z-offset management |\n| **Settings** | `settings.h/cpp` | EEPROM persistence, configuration storage and retrieval |\n| **PrintCounter** | `printcounter.h/cpp` | Print time, filament usage, and job statistics |\n| **Servo** | `servo.h/cpp` | Servo control for solenoids, lid mechanisms |\n| **Delta/Scara/Polar** | `delta.h`, `scara.h`, `polar.h` | Kinematic transforms for non-Cartesian printers |\n| **Tool Change** | `tool_change.h/cpp` | Multi-extruder tool switching, hotend offset, PTFE purge |\n\n### 4.4 Feature System\n\nThe `feature/` directory contains 80+ optional modules, each gated by a `#if ENABLED(FEATURE_NAME)` preprocessor check:\n\n**Motion Features:** Linear Advance (`linearadvance`), Pressure Advance, Adaptive Multi-Axis Stepping, Direct Stepping, Bresenham acceleration, Resonance Compensation, X-Axis Twist Correction, Z-Stepper Alignment, Backlash Compensation.\n\n**Thermal Features:** Automatic PID Tuning (M303), Thermal Protection, Probe Temperature Compensation, Mixed Extruder (`mixing`), Bowden/Direct Drive Retraction (`fwretract`).\n\n**Peripheral Features:** Power Loss Recovery (`powerloss`), Filament Runout Detection (`runout`), Ethernet Connectivity (`ethernet`), RS485 Communication (`rs485`), MMU/Multi- Material Unit (`mmu`, `mmu3`), Solenoid Control (`solenoid`), Spindle/Laser (`spindle_laser`), Case Lights (`caselight`), LED Color Control (`leds`).\n\n**UI Features:** Touch Screen (`touch`), LVGL TFT UI (`mks_ui`), DWIN Display (`dwin`), Extensible UI (`extui`), Password Protection (`password`), Joystick Input (`joystick`).\n\n**Bed Leveling:** Unified Bed Leveling (UBL), Manual Bed Leveling (MBL), Automatic Bed Leveling (ABL), G26 Mesh Validation, G35 Auto Bed Leveling, Bed Level Visualizer.\n\n## 5. G-Code Command Reference\n\nMarlin implements 100+ G-code commands organized into 14 categories under `src/gcode/`:\n\n| Category | Directory | Key Commands | Description |\n| --- | --- | --- | --- |\n| **Motion** | `motion/` | G0, G1, G2, G3, G5, G6, G80, M400, M290 | Linear/arc moves, rapid moves, dwell, wait, jog |\n| **Temperature** | `temp/` | M104/M105/M106/M107/M109, M140/M190, M303 | Hotend/bed temp set/read, fan control, PID autotune |\n| **Configuration** | `config/` | M200-M205, M301, M92, M43, M218, M217 | Extruder steps, accel/jerk, PID, hotend offset, bowden length |\n| **Calibration** | `calibrate/` | G28, G33, G34, G425, M48, M566, M665 | Home, delta calibration, nozzle purge, backlash, bellows |\n| **Geometry** | `geometry/` | G17-G19, G53-G59, G92, M206, M428 | Plane selection, coordinate systems, position reset, offsets |\n| **Control** | `control/` | M17/M18/M84, M80/M81, M3-M5, M7-M9, T | Enable axes, power, spindle, tool select, feedrate/extruder units |\n| **Probe** | `probe/` | G30, G31/G32, G38, M851, M401/M402, M102 | Bed probe, probe type, probe actions |\n| **SD Card** | `sd/` | M20-M34, M928, M1001, M1003 | File listing, select, start, stop, abort, load, save |\n| **LCD** | `lcd/` | M0, M1, M73, M117, M145, M250, M300 | Pause, progress, message, color, beep |\n| **Host** | `host/` | M16, M110, M113, M114, M115, M118, M119, M154, M360, M876 | Line numbers, machine info, endstop report, kinematic config |\n| **EEPROM** | `eeprom/` | M500, M501, M502, M503, M504 | Save/load/reset/settings EEPROM operations |\n| **Stats** | `stats/` | M31, M75-M78 | Print time, filament used, errors, buffer stats |\n| **Units** | `units/` | G20/G21, M82/M83, M149 | mm/inch, extrusion mode, temp unit display |\n| **OTA** | `ota/` | M936 | Over-the-air firmware update |\n\n## 6. Infrastructure & Deployment\n\n### 6.1 Build System\n\nMarlin uses PlatformIO as its primary build system, configured via `platformio.ini`:\n\n- **Framework**: Arduino (via PlatformIO)\n- **Build flags**: `-g3 -D__MARLIN_FIRMWARE__ -DNDEBUG -fsingle-precision-constant`\n- **Source filter**: Dynamic inclusion/exclusion of files based on target board via pre-build scripts (`configuration.py`, `common-dependencies.py`, `preflight-checks.py`)\n- **Include directory**: `Marlin/src/`\n- **Board definitions**: Custom PlatformIO board definitions in `buildroot/share/PlatformIO/boards/`\n\n### 6.2 Configuration System\n\nConfiguration is managed through a layered preprocessor conditional system:\n\n1. **`Configuration.h`** — User-editable hardware and feature configuration\n2. **`Configuration_adv.h`** — Advanced/experimental feature toggles\n3. **`Version.h`** — Build version, machine name, website URL\n4. **`inc/Conditionals-*.h`** — Auto-generated feature flags derived from Configuration.h\n5. **`inc/MarlinConfig.h`** — Prefix header including all conditionals, types, and sanity checks\n\nThe conditional system ensures that only enabled features compile, minimizing firmware footprint for resource-constrained 8-bit platforms.\n\n### 6.3 CI/CD Pipeline\n\nGitHub Actions workflows run on every pull request and push:\n\n| Workflow | Purpose |\n| --- | --- |\n| `ci-build-tests.yml` | Compiles Marlin for all supported boards (matrix strategy) |\n| `ci-unit-tests.yml` | Runs C++ unit tests on the native simulator |\n| `ci-validate-boards.yml` | Validates board pin configurations |\n| `ci-validate-pins.yml` | Checks for missing/invalid pin definitions |\n| `ci-validate-lines.yml` | Validates line count limits for 8-bit boards |\n| `check-pr.yml` | PR quality checks |\n\n### 6.4 Docker Build Environment\n\nA Dockerfile (`docker/Dockerfile`) provides a reproducible build environment:\n\n```dockerfile\nFROM python:3.11-bookworm\nRUN pip install -U platformio PyYaml\nWORKDIR /code\n```\n\n### 6.5 Testing\n\n- **Unit tests**: Located in `Marlin/tests/`, run via the Linux native simulator HAL\n- **Build tests**: CI compiles against 20+ board targets to catch compile errors\n- **Pin validation**: Automated checks for pin conflicts and missing definitions\n- **SdFat**: Integrated SdFat library for SD card file operations (Sd2Card, SdBaseFile, SdVolume)\n\n## 7. Extension Patterns\n\n### 7.1 Adding a New Feature\n\n1. Create your feature files in `Marlin/src/feature/` (e.g., `my_feature.h` and `my_feature.cpp`)\n2. Add `#define MY_FEATURE` to `Configuration_adv.h`\n3. The conditional system in `inc/Conditionals-*.h` will auto-generate `HAS_MY_FEATURE`\n4. Use `#if ENABLED(MY_FEATURE)` guards in your code\n5. Include your header from `MarlinCore.cpp` under the appropriate `#if ENABLED()` block\n6. Register G-code handlers in `gcode/gcode.cpp` or a new category subdirectory\n\n### 7.2 Adding a New G-Code Command\n\n1. Create a new file in the appropriate `src/gcode/<category>/` directory (e.g., `M1000.cpp`)\n2. Implement the handler method in the `GcodeSuite` class\n3. Register the command in the `process_commands()` method of `GcodeSuite`\n4. The command will be dispatched automatically when the parser encounters the letter/code\n\n### 7.3 Adding Board Support\n\n1. Create a new PlatformIO board definition in `buildroot/share/PlatformIO/boards/`\n2. Add pin definitions in `Marlin/src/pins/<family>/pins_<board>.h`\n3. If the MCU is new, add HAL support in `Marlin/src/HAL/<MCU_FAMILY>/`\n4. Update `ini/<platform>.ini` in `platformio.ini`'s `extra_configs`\n5. Run `ci-validate-pins.yml` to verify pin assignments\n\n### 7.4 Adding a Display Backend\n\n1. Create backend files in `Marlin/src/lcd/<backend>/`\n2. Implement the required display interface (SPI/I2C/UART)\n3. Add `#define ULTIPANEL` or display-specific `#define` to `Configuration.h`\n4. The LCD subsystem routes through `MarlinUI` to the appropriate backend\n\n## 8. Rules & Anti-Patterns\n\n### Best Practices\n\n- **Conditional compilation**: Always gate platform-specific code with `#if ENABLED()` or `#ifdef ARDUINO_ARCH_XXX`\n- **PROGMEM usage**: Store large strings and lookup tables in program memory on 8-bit platforms\n- **Interrupt safety**: Use `WAIT`/`NOOP` patterns for stepper ISR synchronization\n- **EEPROM limits**: Respect memory constraints — 8-bit boards have limited EEPROM (4KB-8KB)\n- **Thread safety**: The main loop and stepper ISR share state — use volatile and careful locking\n- **Static analysis**: Use `bug_on()` macros for compile-time assertion checks\n- **Code size**: 8-bit targets (AVR) have strict flash limits — enable only necessary features\n\n### Anti-Patterns\n\n- **Don't add features to `Configuration.h`** — use `Configuration_adv.h` for advanced/experimental features\n- **Don't modify HAL shared code** without understanding platform implications\n- **Don't use floating-point on AVR** without explicit `float` typing — the compiler defaults to single precision\n- **Don't block in the main loop** — all long operations must yield to `idle()` or use non-blocking patterns\n- **Don't assume 32-bit semantics** — code must compile for both 8-bit AVR and 32-bit ARM\n- **Don't hardcode pin numbers** — always use the pin definition headers\n\n## 9. Dependencies\n\n### Build Tools\n\n| Dependency       | Version           | Purpose                    |\n| ---------------- | ----------------- | -------------------------- |\n| PlatformIO       | Latest dev        | Build system and framework |\n| Arduino Core     | Platform-specific | Microcontroller SDK        |\n| GCC ARM Embedded | 12.x              | ARM cross-compiler         |\n| avr-gcc          | 7.x               | AVR cross-compiler         |\n\n### Libraries (integrated)\n\n| Library    | Purpose                                             |\n| ---------- | --------------------------------------------------- |\n| SdFat      | SD card file system (Sd2Card, SdBaseFile, SdVolume) |\n| u8glib     | OLED/LCD display driver (embedded in HAL)           |\n| LVGL       | TFT graphical UI (optional, for MKS UI)             |\n| heatshrink | G-code decompression for SD prints                  |\n\n### Platform Libraries\n\n| Platform | Libraries                     |\n| -------- | ----------------------------- |\n| STM32    | STM32 HAL/LL, STM32duino core |\n| AVR      | Arduino AVR core, TimerOne    |\n| ESP32    | ESP-IDF components, WiFi      |\n| RP2040   | Arduino RP2040 core, PIOasm   |\n| Teensy   | Teensyduino core, Bounce2     |\n| SAMD     | Arduino SAMD core             |\n\n## 10. Code Structure\n\n```\nMarlinFirmware/                  # Repo root (this directory). `cd` here for PlatformIO builds.\n├── Marlin/                      # The Arduino \"sketch\" (application source + config files)\n│   ├── Configuration.h          # User hardware configuration\n│   ├── Configuration_adv.h      # Advanced/experimental toggles\n│   ├── Marlin.ino               # Board-specific entry stub\n│   ├── Makefile                 # Alternative build (simulator)\n│   ├── Version.h                # Build version, machine name\n│   ├── config.ini               # PlatformIO extra config\n│   ├── lib/                     # Integrated libraries\n│   └── src/                     # Application source (see below)\n├── buildroot/                   # Build infrastructure\n├── ini/                         # PlatformIO platform configs\n├── docs/                        # Project documentation\n├── docker/                      # Docker build environment\n├── .github/workflows/           # CI/CD pipelines\n└── platformio.ini               # Main PlatformIO configuration\n```\n\n### Marlin/src layout\n\n```\nMarlin/src/\n├── MarlinCore.cpp/h         # Firmware entry, setup(), loop(), Marlin singleton\n├── core/                    # Core utilities (serial, language, types, mstring)\n│   ├── gcode/                   # G-code subsystem\n│   │   ├── gcode.cpp/h          # GcodeSuite dispatcher\n│   │   ├── parser.cpp/h         # GCodeParser\n│   │   ├── queue.cpp/h          # GCodeQueue ring buffer\n│   │   ├── motion/              # G0-G6, M290, M400\n│   │   ├── temp/                # M104-M109, M140-M193, M303\n│   │   ├── config/              # M200-M205, M301, M92, M218\n│   │   ├── calibrate/           # G28, G33, G34, G425, M48\n│   │   ├── bedlevel/            # G26, G35, G42, M420\n│   │   ├── geometry/            # G17-G19, G53-G59, G92\n│   │   ├── probe/               # G30-G38, M851, M401\n│   │   ├── sd/                  # M20-M34, M928\n│   │   ├── lcd/                 # M0, M1, M73, M117\n│   │   ├── host/                # M16, M110-M119\n│   │   ├── eeprom/              # M500-M504\n│   │   ├── stats/               # M31, M75-M78\n│   │   ├── units/               # G20/G21, M82/M83\n│   │   └── control/             # M3-M5, M7-M9, M17-M85, T\n│   ├── module/                  # Core modules\n│   │   ├── motion.h/cpp         # Axis positioning, kinematics\n│   │   ├── planner.h/cpp        # Block buffer, trapezoidal profiling\n│   │   ├── stepper.h/cpp        # Step pulses, TMC drivers\n│   │   ├── temperature.h/cpp    # PID, thermistors, safety\n│   │   ├── endstops.h/cpp       # Endstop interrupts, homing\n│   │   ├── probe.h/cpp          # Bed probing, Z-offset\n│   │   ├── settings.h/cpp       # EEPROM persistence\n│   │   ├── delta.h/cpp          # Delta kinematics\n│   │   ├── scara.h/cpp          # Scara kinematics\n│   │   ├── polar.h/cpp          # Polar kinematics\n│   │   ├── polargraph.h/cpp     # Polar graph kinematics\n│   │   ├── servo.h/cpp          # Servo control\n│   │   ├── printcounter.h/cpp   # Print statistics\n│   │   ├── tool_change.h/cpp    # Multi-extruder switching\n│   │   ├── ft_motion/           # Fine-tune motion\n│   │   └── stepper/             # Stepper internals, Trinamic\n│   ├── feature/                 # 80+ optional features\n│   │   ├── bedlevel/            # UBL, MBL, ABL\n│   │   ├── leds/                # LED color control\n│   │   ├── mmu/                 # Multi-material unit\n│   │   ├── mmu3/                # MMU3 variant\n│   │   ├── powerloss.h/cpp      # Power loss recovery\n│   │   ├── runout.h/cpp         # Filament runout detection\n│   │   ├── pause.h/cpp          # Pause/resume\n│   │   ├── mixing.h/cpp         # Mixed extruder\n│   │   ├── fwretract.h/cpp      # Firmware retraction\n│   │   ├── resonance/           # Resonance compensation\n│   │   ├── spindle_laser.h/cpp  # Spindle/laser control\n│   │   ├── ethernet.h/cpp       # Ethernet connectivity\n│   │   ├── solenoid.h/cpp       # Solenoid control\n│   │   ├── password/            # Password protection\n│   │   ├── joystick.h/cpp       # Joystick input\n│   │   ├── tmc_util.h/cpp       # TMC driver utilities\n│   │   └── ...                  # 60+ more features\n│   ├── lcd/                     # Display/UI subsystem\n│   │   ├── marlinui.h/cpp        # Main UI controller\n│   │   ├── HD44780/             # Hitachi HD44780 LCD\n│   │   ├── dogm/                # DOGM OLED/LCD\n│   │   ├── tft/                 # TFT displays\n│   │   ├── tft_io/              # TFT I/O drivers\n│   │   ├── dwin/                # DWIN displays\n│   │   ├── extui/               # Extensible UI (MKS LVGL)\n│   │   ├── menu/                # Menu system\n│   │   ├── touch/               # Touch screen\n│   │   ├── language/            # Multi-language strings\n│   │   └── sovol_rts/           # Sovol RTS display\n│   ├── libs/                    # Utility libraries\n│   │   ├── bresenham.h          # Bresenham line algorithm\n│   │   ├── vector_3.h/cpp       # 3D vector math\n│   │   ├── circularqueue.h      # Generic circular queue\n│   │   ├── adc/                 # ADC utilities\n│   │   ├── heatshrink/          # Decompression\n│   │   ├── nozzle.cpp/h         # Nozzle cleaning\n│   │   ├── numtostr.h/cpp       # Number-to-string\n│   │   ├── hex_print.h/cpp      # Hex printing\n│   │   ├── crc16.h/cpp          # CRC-16 calculation\n│   │   ├── stopwatch.h/cpp      # Stopwatch utility\n│   │   ├── least_squares_fit.h  # Least squares fitting\n│   │   └── ...                  # 15+ utility libs\n│   ├── sd/                      # SD card subsystem\n│   │   ├── cardreader.h/cpp     # Card reader interface\n│   │   ├── SdFat*               # SdFat library (file system)\n│   │   ├── disk_io_driver.h     # Disk I/O abstraction\n│   │   └── usb_flashdrive/      # USB flash drive support\n│   ├── pins/                    # Pin definitions (400+ files)\n│   │   ├── pins.h               # Pin inclusion router\n│   │   ├── ramps/               # Ramps board family\n│   │   ├── mega/                # Mega board family\n│   │   ├── rambo/               # Rambo board family\n│   │   ├── sanguino/            # Sanguino board family\n│   │   ├── stm32f1/             # STM32F1 boards\n│   │   ├── stm32f4/             # STM32F4 boards\n│   │   ├── stm32f7/             # STM32F7 boards\n│   │   ├── stm32h7/             # STM32H7 boards\n│   │   ├── stm32g0/             # STM32G0 boards\n│   │   ├── stm32f0/             # STM32F0 boards\n│   │   ├── sam/                 # SAM boards\n│   │   ├── samd/                # SAMD boards\n│   │   ├── rp2040/              # RP2040 boards\n│   │   ├── lpc1768/             # LPC1768 boards\n│   │   ├── lpc1769/             # LPC1769 boards\n│   │   ├── teensy2/             # Teensy 2.x boards\n│   │   ├── teensy3/             # Teensy 3.x boards\n│   │   ├── teensy4/             # Teensy 4.x boards\n│   │   ├── esp32/               # ESP32 boards\n│   │   ├── gd32f1/              # GD32F1 boards\n│   │   ├── gd32f3/              # GD32F3 boards\n│   │   ├── hc32f4/              # HC32F4 boards\n│   │   ├── at32f4/              # AT32F4 boards\n│   │   └── native/              # Native/simulator\n│   ├── HAL/                     # Hardware Abstraction Layer\n│   │   ├── HAL.h                # HAL interface definition\n│   │   ├── platforms.h          # Platform selection\n│   │   ├── shared/              # Cross-platform HAL code\n│   │   │   ├── HAL.cpp/h        # Shared HAL implementation\n│   │   │   ├── Delay.h          # Safe delay function\n│   │   │   ├── eeprom_api.h     # EEPROM API\n│   │   │   ├── cpu_exception/   # Exception handling\n│   │   │   └── backtrace/       # Stack backtrace\n│   │   ├── STM32/               # STM32 platform HAL\n│   │   ├── AVR/                 # AVR platform HAL\n│   │   ├── ESP32/               # ESP32 platform HAL\n│   │   ├── DUE/                 # Arduino Due HAL\n│   │   ├── SAMD21/              # SAMD21 HAL\n│   │   ├── SAMD51/              # SAMD51 HAL\n│   │   ├── RP2040/              # RP2040 HAL\n│   │   ├── LPC1768/             # LPC1768 HAL\n│   │   ├── GD32_MFL/            # GD32 HAL\n│   │   ├── HC32/                # HC32 HAL\n│   │   ├── AT32/                # AT32 HAL\n│   │   ├── TEENSY31_32/         # Teensy 3.1/3.2 HAL\n│   │   ├── TEENSY35_36/         # Teensy 3.5/3.6 HAL\n│   │   ├── TEENSY40_41/         # Teensy 4.0/4.1 HAL\n│   │   ├── STM32F1/             # STM32F1 HAL\n│   │   ├── LINUX/               # Linux simulator HAL\n│   │   └── NATIVE_SIM/          # Native simulator HAL\n│   └── tests/                   # Unit tests\n│       ├── unit_tests.h/cpp     # Test suite\n│       └── *.ini                # Test configurations\n├── docs/                        # Project documentation\n│   ├── AGENTS.md                # This document\n│   └── diagrams/                # Architecture diagrams\n│       ├── high-level-architecture.drawio\n│       ├── processing-pipeline.drawio\n│       └── component-relationships.drawio\n├── buildroot/                   # Build infrastructure\n│   ├── share/PlatformIO/        # PlatformIO board definitions & scripts\n│   ├── test-gcode/              # G-code test files\n│   └── tests/                   # Build test configurations\n├── ini/                         # PlatformIO platform configs\n│   ├── avr.ini, due.ini, esp32.ini\n│   ├── stm32-common.ini, stm32f1.ini, stm32f4.ini\n│   ├── stm32f7.ini, stm32h7.ini, stm32g0.ini\n│   ├── samd21.ini, samd51.ini\n│   ├── raspberrypi.ini, teensy.ini\n│   ├── at32.ini, gd32.ini, hc32.ini\n│   ├── lpc176x.ini, native.ini\n│   ├── features.ini, renamed.ini\n│   └── stm32f1-maple.ini\n├── test/                        # Root-level test configurations\n├── docker/                      # Docker build environment\n├── .github/workflows/           # CI/CD pipelines\n└── platformio.ini               # Main PlatformIO configuration\n```\n\n## 11. Constructing a Pull Request\n\nMarlin accepts AI-assisted code (see `.github/contributing.md`). It must be sensible and reviewable. Follow these rules when preparing a PR.\n\n### Scope and size\n\n- **One PR, one purpose.** A PR should be a single self-contained change that builds and passes CI on its own.\n- **Split large changes.** If a change touches several unrelated areas, or is large enough that a reviewer can't hold it in their head, split it into two (or more) smaller PRs. Typical splits: refactor first, feature second; core logic first, UI/LCD support second; one HAL or board family per PR; sanity checks and docs with the feature they cover.\n- **Order dependent PRs.** If PR B needs PR A, say so in B's description and don't open B until A is far enough along to be reviewed.\n- **No drive-by changes.** Don't reformat, rename, or tidy code you aren't changing. Keep the diff minimal.\n- **Don't change defaults** in `Configuration.h` / `Configuration_adv.h` unless that is the purpose of the PR. New features are disabled by default.\n\n### Before you start\n\n- Branch from the latest `bugfix-2.1.x` (the working branch). Never target release branches.\n- We occasionally backport fixes to lts-* branches. Very small non-duplicate patches are accepted.\n- Read the neighboring code and follow its conventions. Check `buildroot/skills/` for step-by-step procedures.\n- Read the HAL-specific `AGENTS.md` (`Marlin/src/HAL/<FAMILY>/AGENTS.md`) when touching a HAL.\n\n### Making the change\n\n- Code must compile on 8-bit AVR and 32-bit targets. Mind flash and RAM on AVR. Keep the base build requirements from growing.\n- Gate optional code with `ENABLED()` / `DISABLED()` / `ANY()` / `NONE()` / etc. and add validation to `Marlin/src/inc/SanityCheck.h`.\n- Use a suitable insertion point for Configuration and Conditionals. They follow a strict order set by `Marlin/src/inc/MarlinConfig.h`.\n- Don't hardcode pins; use the pin headers in `src/pins`.\n- When adding or changing a user-facing option, update its comments in `Configuration*.h` and note any `Conditionals`, `Warnings.cpp`, or `config.ini` impacts.\n- Follow the Marlin coding standards (2-space indent, no tabs, spacing as in nearby code). Indent new code similar to surroundings (including preprocessor nesting) and avoid auto-formatters.\n\n### Verifying\n\n- Use `MarlinSimUI` for direct testing in a simulated environment with a rich MCP interface. (And contribute your enhancements to `MarlinSimUI`.)\n- Build at least one affected environment (`buildroot/bin/mftest`; see the `marlin-build-test` skill).\n- **Add tests with new features and options.** Add or update a `buildroot/tests/<env>/config-NN.ini` build test so CI exercises the feature, along with its dependent options.\n- Add a unit test in `Marlin/tests/<area>/` (run with `make unit-test-all-local`) when the logic can be checked without hardware.\n  - Bug fixes should get a test when one can catch the regression. See the `marlin-build-test` skill.\n- Report honestly what was and wasn't tested. Never claim a build or test passed unless you ran it.\n\n### Commit messages and PR description\n\n- Imperative mood, first line of 72 characters or fewer, prefixed with a gitmoji-style emoji as in `git log` (e.g. 🐛 fix, ✨ feature, ⚡️ performance, 🔧 config, 🩹 minor fix, 🧑‍💻 developer tooling, 🚸 UX).\n- Don't put issue numbers in the PR title. Reference them in the description.\n- Fill in every section of `.github/pull_request_template.md`: Description, Requirements, Benefits, Configurations, Related Issues.\n- Keep history clean: rebase on `bugfix-2.1.x` rather than merging, and squash fixups into the commits they belong to.\n\n### Skills\n\nReusable procedures for contributors and their tools live in `buildroot/skills/`. Each skill is a folder with a `SKILL.md`. See `buildroot/skills/README.md`.\n\n<!-- File: buildroot/skills/marlin-build-test/SKILL.md (Tokens: ~1187 | Category: anthropic-skill) -->\n---\nname: marlin-build-test\ndescription: Use when you need to compile Marlin for a board, apply a CI test configuration, or run the build tests locally. Wraps buildroot/bin/mftest, mfenvs, run_tests, and the Makefile targets.\n---\n\n# Build and Test Marlin\n\nRequires PlatformIO (`pio`) in your `PATH`. Run everything from the repo root.\n\n## Build the configured board\n\n1. Set `MOTHERBOARD` in `Marlin/Configuration.h` and set up your options.\n2. List the PlatformIO environments for the board: `buildroot/bin/mfenvs`\n3. Build the auto-detected environment: `buildroot/bin/mftest -a` (rebuild with `-r`, silent with `-s`).\n4. Or build one environment directly: `pio run -e <env>`\n\n## Apply a CI test config and build it\n\nTests live in `buildroot/tests/<env>/config-NN.ini` and are applied with the `opt_*` tools.\n\n- Interactive menu: `buildroot/bin/mftest`\n- Specific test: `buildroot/bin/mftest -t mega2560 -n 5 -y` (`-t` env name or shortcut, `-n` test index, `-y` build without asking)\n- Start from default configs first: add `-d`\n- Only emit the config changes without building: add `-i`\n- Use the Docker/make method: add `-m`\n\nShortcuts for `-t`: `tree due esp lin lp8 lp9 m128 mega stm f1 f4 f7 s6 teensy t31 t35 t40`.\n\n## Add tests for a new feature or option\n\nA PR that adds a feature or option should add tests, so CI keeps it building and working.\n\n**Build test** (`buildroot/tests/<env>/config-NN.ini`)\n\n1. Pick the env directory that matches a board and platform that can build the feature. Use `buildroot/bin/mfenvs` or the existing directories under `buildroot/tests`.\n2. Prefer adding the option to an existing `config-NN.ini` with a similar feature set. Add a new file only for a combination that no existing test covers. Use the next free number.\n3. Each file begins with a header (`# Test: <env>/config-NN.ini`, then a one-line description of the main options) and a `[config:base]` section. Start from an example with `ini_use_config = example/...`, or leave it out for the defaults.\n4. Set options as `name = value` or `name = on`. Test the feature enabled along with the options it depends on, and with the dependent sub-options.\n5. Run just your test: `buildroot/bin/mftest -t <env> -n <index> -y` or `make tests-single-local TEST_TARGET=<env> ONLY_TEST=<index>`, where `<index>` is the test's position in file order (1-based) and normally matches `NN`.\n6. Keep the number of tests small. Every test adds to CI time.\n\n**Unit test** (when the logic can be checked without hardware)\n\nUnit tests run natively on the `linux_native_test` environment. Good candidates are parsing, math, conversions, state machines and G-code handling. Anything that needs real pins, timers, or devices isn't a good candidate. See `test/README.md`.\n\n1. Add `Marlin/tests/<area>/test_<name>.cpp`. Copy the license header and the `MARLIN_TEST(group, name)` macros from an existing test such as `Marlin/tests/feature/test_runout.cpp`.\n2. Wrap the file's contents in `#if ENABLED(FEATURE)` so it compiles only for configs that enable it.\n3. If no existing config in `test/*.ini` enables your feature, add one (`test/NNN-name.ini`). It must use `motherboard = BOARD_SIMULATED` and include only the options needed to pass the sanity checks.\n4. Run: `make unit-test-all-local`, or one config with `make unit-test-single-local UNIT_TEST_CONFIG=<name>`. (There is a `-docker` variant of each.)\n\n## Run the tests like CI does\n\n- One environment: `make tests-single-local TEST_TARGET=mega2560`\n- One test of it: `make tests-single-local TEST_TARGET=mega2560 ONLY_TEST=2`\n- Everything (slow): `make tests-all-local`\n- In Docker: `make tests-single-local-docker TEST_TARGET=mega2560` (see `make help`)\n- Unit tests: `make unit-test-all-local`\n\nThe test runner restores the configs between tests and may `git reset --hard` (see `GIT_RESET_HARD` in `make help`), so commit or stash your work first.\n\n## Restore configuration files\n\nAfter testing, run `buildroot/bin/restore_configs`. It deletes generated files (`marlin_config.json`, `_Bootscreen.h`, `_Statusscreen.h`) and does a `git checkout` of the configuration files, discarding any uncommitted edits to them. `restore_configs -d` instead loads the default example configs.\n\n## Choosing what to build\n\n- Build at least one environment each for any HAL or board family you changed.\n- A change to common code deserves an AVR build (tight flash) and a 32-bit build.\n- For features, build with the feature both enabled and disabled.\n- New feature or option: add a build test and, where it makes sense, a unit test (see above).\n\n## Report results\n\nState exactly which envs and tests you built. Do not claim a pass that you did not see.\n\n<!-- File: buildroot/skills/marlin-code-style/SKILL.md (Tokens: ~395 | Category: anthropic-skill) -->\n---\nname: marlin-code-style\ndescription: Use before committing C/C++ changes to Marlin, to match project conventions and indentation in new code without reformatting existing code.\n---\n\n# Marlin Code Style\n\nFull standards: https://marlinfw.org/docs/development/coding_standards.html\n\n## Rules of thumb\n\n- Match the file you are editing: indentation (2 spaces, no tabs), brace placement, spacing, naming.\n- Code inside preprocessor blocks (`#if` / `#else` / `#endif`) is indented one level per nesting depth, the same as code inside braces. Copy how nearby code does it.\n- Use `ENABLED(FEATURE)` / `DISABLED(FEATURE)` for options, and `#if HAS_...` for derived capabilities.\n- Keep optional code behind its feature flag so it costs nothing when disabled.\n- Code must build on 8-bit AVR as well as 32-bit targets. Avoid unnecessary `double`, `String`, and heap allocation.\n- Comments should say why, concisely. End every file with a newline. No trailing whitespace.\n\n## Don't reformat existing code\n\n- Existing code may have ad hoc alignment and spacing (aligned columns, grouped one-liners). Leave it alone.\n- Don't re-indent, re-wrap, or re-space lines you aren't otherwise changing. Keep diffs small and focused.\n- Exception: It's good to keep related eol comments aligned.\n- Review `git diff` before committing and revert any whitespace-only churn.\n\n## Auto-formatters\n\nDon't run the auto-formatter on existing files. `buildroot/bin/format_code` and `uncrust` (uncrustify) get close to the Marlin style but don't match it exactly, and will produce unwanted churn. If you use one, apply it only to a scratch copy to see suggestions, then hand-apply what is correct to your own new lines.\n\n<!-- File: buildroot/skills/marlin-codebase-navigation/SKILL.md (Tokens: ~1351 | Category: anthropic-skill) -->\n---\nname: marlin-codebase-navigation\ndescription: Use when you need to find where something is defined, what uses it, or how a command or setting flows through Marlin's source. Uses only git, grep, and the repo layout, with optional tools as extras.\n---\n\n# Navigate the Marlin Source\n\nMarlin is a large C++ tree where most code is compiled only under certain options. These steps work with nothing but `git` and a shell. Better tools are listed at the end as optional.\n\nFor the layout, singletons, and processing pipeline, read `AGENTS.md` (sections 2 to 4 and 10) first. This skill covers how to search, and where the common things live.\n\n## Where things live\n\n| Looking for | Look in |\n| --- | --- |\n| A G-code command `Mnnn` / `Gnnn` | `git grep -n \"GcodeSuite::M913\"`. Handlers are in `Marlin/src/gcode/<group>/`, and some files hold several (`M911-M914.cpp`). The dispatch `switch` is in `Marlin/src/gcode/gcode.cpp`, and declarations are in `gcode/gcode.h` |\n| What runs each loop | `setup()` and `loop()` in `Marlin/src/MarlinCore.cpp` |\n| Motion, planner, stepper ISR | `Marlin/src/module/` (`motion`, `planner`, `stepper`) |\n| Temperature, PID, MPC | `Marlin/src/module/temperature.cpp` |\n| Optional feature code | `Marlin/src/feature/` and `gcode/feature/` |\n| An option's derived flags (`HAS_*`) | `Marlin/src/inc/Conditionals-*.h` |\n| Why a config combination is rejected | `Marlin/src/inc/SanityCheck.h`; renamed options are in `Changes.h` |\n| A board's pins | `Marlin/src/pins/<family>/pins_<BOARD>.h`; the `#elif MB(...)` chain is in `pins/pins.h`; the ID is in `core/boards.h` |\n| Platform-specific code | `Marlin/src/HAL/<FAMILY>/`, and that folder's `AGENTS.md` |\n| An LCD string | `Marlin/src/lcd/language/language_en.h` (`MSG_*`) |\n| A menu screen | `Marlin/src/lcd/menu/menu_*.cpp` |\n| A serial or touch display | `Marlin/src/lcd/extui/` |\n| Which source folders a build includes | `ini/features.ini` and `ini/*.ini` |\n| A CI test config | `buildroot/tests/<env>/config-NN.ini` |\n\n## Search recipes\n\n    git grep -n \"OPTION_NAME\" -- Marlin                  # every use, including config comments\n    git grep -n \"SOME_OPTION\" -- 'Marlin/src/inc/*'      # where it is derived or validated\n    git grep -nE \"\\bfunction_name\\(\" -- Marlin/src       # calls and definitions\n    git grep -n \"STR_SOMETHING\\|MSG_SOMETHING\" -- Marlin/src/lcd\n    git log -S\"suspicious_string\" --oneline -- Marlin/src   # when it was introduced or removed\n    git log --oneline -20 -- Marlin/src/module/motion.cpp   # recent changes to a file\n    git blame -L 120,160 Marlin/src/module/motion.cpp       # who changed these lines, then read that PR\n\n## Tracing a setting or command\n\n1. Find the option in `Marlin/Configuration.h` or `Configuration_adv.h`.\n2. `git grep` it under `Marlin/src/inc` to see what it enables and what it conflicts with.\n3. `git grep` it under the rest of `Marlin/src` to find the code it gates.\n4. For a G-code, go from the `case` in `gcode/gcode.cpp` to its handler, then follow the calls into `module/`.\n5. If a symbol seems to have no users, check for macro use (`TERN`, `ENABLED`) and for different spellings under other options before deciding it is dead.\n\n## Pitfalls\n\n- **Most code is conditional.** A match inside `#if ENABLED(X)` is not compiled unless `X` is on. Read the enclosing `#if`s, and build with the option on and off.\n- **Macros hide calls.** `SERIAL_ECHO*`, `TERN*`, `LOOP_*`, and `HAL_PATH` includes do not look like ordinary function calls, so search for the macro name too.\n- **Old issues and forks use old paths.** `ultralcd.*` is now `marlinui.*`, and `Conditionals_post.h` was split into the numbered files. Search the current name before applying advice from an old thread.\n- **Line numbers drift.** Quote a symbol and a file, not only a line number, in a PR or comment.\n- **IDE diagnostics can mislead.** Marlin builds with GCC. A Clang-based language server often reports errors on headers that build fine. Trust the build (skill: marlin-build-test).\n\n## Optional tools\n\nNone of these are required, and none replace reading the code.\n\n- `rg` (ripgrep) is faster than `git grep` on the whole tree and handles the same patterns.\n- Your AI tool's own search (for example Claude Code's file and content search, or a code-exploration sub-agent) works well for broad questions such as \"where is homing decided\". Check its answer with `git grep`.\n- A symbol index such as `universal-ctags` helps with jump-to-definition in an editor. Because of the `#if` blocks it can show several definitions of one name.\n- A code-knowledge-graph MCP server (for example codebase-memory-mcp) can locate definitions, line ranges, and function size from a natural-language query. In testing on Marlin, its call-graph results were incomplete: callers came back at file level and macro-dispatched code had no callees. Use it to locate code and `git grep` to find every use. It indexes a snapshot of the checked-out tree, so re-index after switching branches.\n- If you try a language-server plugin, expect noise from the heavy preprocessor use and keep a real build as the final check.\n\n<!-- File: buildroot/skills/marlin-config-options/SKILL.md (Tokens: ~461 | Category: anthropic-skill) -->\n---\nname: marlin-config-options\ndescription: Use when you need to find, enable, disable, or set Marlin configuration options (Configuration.h / Configuration_adv.h) from the command line. Wraps buildroot/bin/opt_find, opt_enable, opt_disable, opt_set, and opt_add.\n---\n\n# Marlin Configuration Options from the Command Line\n\nThese scripts edit `Marlin/Configuration.h` and `Marlin/Configuration_adv.h` in place. Run them from the repo root. Commit or stash your own edits first so the changes are easy to revert, or run `buildroot/bin/restore_configs` afterward (which does a `git checkout` of the config files, discarding uncommitted edits to them).\n\n## Find\n\n    buildroot/bin/opt_find LIN_ADV         # enabled or disabled options matching the name\n    buildroot/bin/opt_find -e BABYSTEP     # only enabled\n    buildroot/bin/opt_find -d BABYSTEP     # only disabled\n\n## Enable / disable\n\n    buildroot/bin/opt_enable LIN_ADVANCE ADAPTIVE_STEP_SMOOTHING\n    buildroot/bin/opt_disable ADAPTIVE_STEP_SMOOTHING\n\n`opt_enable` exits with an error if an option can't be found.\n\n## Set a value\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_RAMPS_14_EFB EXTRUDERS 2\n\nTakes `<name> <value>` pairs. An option that does not exist is added to `Configuration.h`.\n\n## Use example configurations\n\n    buildroot/bin/use_example_configs Creality/CR-10/CrealityV1\n\nFetches the named example from the Configurations repo (branch matched to your working branch, otherwise `bugfix-2.1.x`). Use `release-2.0.9.4:Creality/CR-10/CrealityV1` to pick a branch or tag.\n\n## Tips\n\n- Option names are matched exactly by enable/disable/set. Use `opt_find` first.\n- Some options need others (see `Marlin/src/inc/SanityCheck.h`). A build error from a sanity check tells you what is missing.\n- The CI test files in `buildroot/tests/` use these same operations. Read a few as examples.\n\n<!-- File: buildroot/skills/marlin-extui-backends/SKILL.md (Tokens: ~1562 | Category: anthropic-skill) -->\n---\nname: marlin-extui-backends\ndescription: Use when adding or changing an ExtUI display backend (DGUS, Anycubic, Nextion, Malyan, FTDI EVE, Neptune 3, IA Creality, serial panels) or the ExtUI API in ui_api.h / ui_api.cpp.\n---\n\n# Marlin ExtUI Backends\n\nExtUI (`Marlin/src/lcd/extui/ui_api.{h,cpp}`) is the API used by serial and touch displays. Backends live in subfolders of `Marlin/src/lcd/extui/`. `example/example.cpp` is the template for a new one.\n\n## Rules\n\n1. **Event handlers (`ExtUI::on*()`) have weak, empty defaults in `ui_api.cpp`.** A backend defines only the handlers it uses. Don't add empty `{}` stubs to a backend, and delete them if you see them. A new handler declared in `ui_api.h` gets exactly one weak empty default in the `WEAK_EXTUI` block in `ui_api.cpp`, inside the same `#if` guard as its declaration. No backend needs to change.\n2. **API functions (`get*` / `set*`) are not event handlers.** They get one real implementation in `ui_api.cpp`, next to the matching getter, and are never weak or inside a backend. A setter moved into the backends leaves empty stubs that silently do nothing.\n3. **Keep the `ExtUI` namespace flat.** Don't use the `using namespace Base` override pattern from `language_*.h`: `ui_api.h` already declares every handler in `ExtUI`, and name hiding breaks overloads such as `onUserConfirmRequired` and `onMeshUpdate`.\n4. **Backends keep to their own folder.** Add the folder to `ini/features.ini` (`build_src_filter=+<src/lcd/extui/<folder>>`), or it will be silently excluded and the link fails with every callback undefined.\n5. **Wire the new LCD define in `Conditionals-2-LCD.h`** (the `IS_EXTUI` list, the `LCD_IS_SERIAL_HOST` list if it is a serial display, and the \"only one LCD\" count), and add its checks to `SanityCheck.h`. Copy a similar backend's entries.\n6. **Handlers must match the current `ui_api.h` signatures.** Backends ported from forks often use renamed or removed handlers. Diff against the full callback list in `ui_api.h` before building.\n7. **After a bulk deletion, tidy up.** Remove runs of blank lines, a blank line directly after `#if` or before `#endif`, orphaned comments, and emptied `#if` blocks. Diff against the base and check that the only `+` lines are the ones you intended.\n\n## A panel that speaks G-code (PanelDue and similar)\n\nTreat it as an ExtUI backend that owns `LCD_SERIAL`, with the whole protocol inside the backend. Add it to `LCD_IS_SERIAL_HOST` (which auto-assigns `LCD_SERIAL_PORT`) rather than routing it through `SERIAL_PORT_2`.\n\n- `LCD_SERIAL` is never a G-code source. The queue reads only `NUM_SERIAL` ports, and `SERIAL_ECHO*` never reaches `LCD_SERIAL`. The backend must call `LCD_SERIAL.begin(LCD_BAUDRATE)` in `onStartup()`, read lines in `onIdle()`, and write replies to `LCD_SERIAL` itself, as Malyan does.\n- Answer status queries directly from `onIdle()`, never through the queue. `idle()` keeps running during `G28`, `M109`, and SD prints, so the panel stays connected while Marlin is busy.\n- Run other commands by injection. `queue.inject()` overwrites the single `injected_commands` buffer (64 bytes), so keep your own FIFO. Hand over whole lines only when `queue.injected_commands[0] == 0`. Send a line too long for the buffer with `queue.enqueue_one()` and retry if that fails.\n- Apply temperature, fan, speed, and flow changes with the ExtUI setters, so they take effect even during a blocking `M109`.\n- Buffer replies and drain them with `LCD_SERIAL_TX_BUFFER_FREE()` (`SERIAL_GET_TX_BUFFER_FREE()` on ESP32 and RP2040). A 1 KB reply takes about 180 ms at 57600 baud, which is too long to block `idle()` during a print.\n- Keep each JSON or status reply on one line. Clients such as PanelDue discard a partial object when they see a newline.\n- If the panel has its own buzzer, add it to the `USE_MARLINUI_BUZZER` list in `Conditionals-5-post.h` and define `MarlinUI::buzz()` in the backend. Without a `BEEPER_PIN`, `HAS_SOUND` is off and `M300` isn't compiled.\n\n## Verify\n\n1. Build every backend the change touches. Find the tests that cover them:\n\n       grep -rliE \"elegoo_neptune_3_tft|dwin_lcd_proui|touch_ui_ftdi_eve|extui_example|dgus_lcd_ui\" buildroot/tests\n\n   Build one with `buildroot/bin/mftest -t <env> -n <N> -y`.\n\n2. Backends with no CI test need a manual build. Use an STM32 board, not AVR RAMPS, because serial LCDs on RAMPS fail on a pin conflict with Serial Port 3:\n\n       buildroot/bin/restore_configs\n       buildroot/bin/opt_set MOTHERBOARD BOARD_BTT_SKR_PRO_V1_1 SERIAL_PORT -1 LCD_SERIAL_PORT 1\n       buildroot/bin/opt_enable EEPROM_SETTINGS SDSUPPORT AUTO_BED_LEVELING_BILINEAR FIX_MOUNTED_PROBE Z_SAFE_HOMING PIDTEMPBED\n       buildroot/bin/opt_enable MALYAN_LCD            # or the backend's own option(s)\n       buildroot/bin/mftest -a -n1 -y\n\n   Some backends need extra options (for example `LCD_BED_TRAMMING` and `BABYSTEPPING` for Anycubic, `CLASSIC_JERK` for DGUS E3S1PRO). Read the first error.\n\n3. Judge each build by its log: count `[SUCCESS]` and `error:`. Don't pass `-s` to `mftest`, which hides the log. Run builds one at a time in a single clone, since parallel builds collide on the `.pio` state.\n4. Confirm an override really happened. With the ARM toolchain's `nm`:\n\n       arm-none-eabi-nm -C .pio/build/<env>/firmware.elf | grep -E ' ExtUI::on[A-Za-z]+\\('\n\n   `T` is the backend's own definition, and `W` is the weak default. A handler you defined that still shows `W` wasn't compiled, so check the guards around it.\n\n5. Run `buildroot/bin/restore_configs` between builds and at the end. `git status --short` should then show only your source edits.\n6. For a serial-panel backend, also run it end to end in the simulator with a scripted panel emulator. Check that replies are valid single lines, that the panel is still answered while Marlin is busy, and that each translated command takes effect.\n\n<!-- File: buildroot/skills/marlin-fork-harvesting/SKILL.md (Tokens: ~1335 | Category: anthropic-skill) -->\n---\nname: marlin-fork-harvesting\ndescription: Use when evaluating a GitHub fork of Marlin (a vendor's or a community member's) and porting a feature, board, or display driver into mainline as a clean, reviewable PR.\n---\n\n# Port a Feature from a Marlin Fork\n\nForks often hold support for hardware that mainline lacks. Their code can't be merged as-is: it is based on an older Marlin, may restructure the HALs, and may not follow the project style. Port the part that belongs in mainline, and port it cleanly.\n\nCheck the fork's license and keep attribution. Say in the PR where the code came from and credit the author.\n\n## 1. Decide whether it is worth porting\n\n| Question | How to check |\n| --- | --- |\n| Is it maintained? | Last push date |\n| Is it really Marlin? | It has `Marlin/src/` |\n| Is it already in mainline? | Search the Marlin PRs and `git log` for the board or feature |\n| Does it add hardware or features mainline lacks? | See step 3 |\n| Is the quality reasonable? | Read two or three key files |\n\nThe vendor's own repo is a secondary source. It is often a frozen snapshot from the product launch, based on an old Marlin, with stale board IDs and an outdated layout. Use it only to cross-check pin assignments and names, not as a port base.\n\n## 2. Add the fork as a remote\n\n    git remote add <author-slug> https://github.com/<Author>/Marlin\n    git fetch <author-slug>\n    git branch -r | grep <author-slug>\n\n## 3. Find only the relevant changes\n\nA fork that diverged long ago shows thousands of changed files, nearly all of it mainline drift. Filter to what matters:\n\n    git log --oneline upstream/bugfix-2.1.x..<slug>/<branch> | wc -l\n    git diff --name-only upstream/bugfix-2.1.x <slug>/<branch> | grep -iE '<machine>|src/pins/|src/HAL/|src/lcd/'\n    git show <slug>/<branch>:Marlin/Configuration.h | grep -E 'MOTHERBOARD|MACHINE_NAME'\n\nCheck each of these:\n\n- **New HAL?** If the board needs an MCU family that mainline doesn't support, decide with the maintainers whether the HAL is part of this PR or a separate one before you write code.\n- **Board entry?** Check `Marlin/src/core/boards.h`. A new board needs a `boards.h` entry, a `pins.h` entry, and a pins file.\n- **UI code?** Large display-driver or UI changes should go in a separate PR from the core feature.\n\n## 4. Port, don't cherry-pick\n\nMake a clean-room port in most cases. Read the fork's code, then write it in mainline's style on a fresh branch:\n\n    git checkout -b <topic> upstream/bugfix-2.1.x\n\nCherry-pick only when the commits are linear, clean, and conflict-free.\n\nNever port a fork's HAL restructuring (deleted platforms, renamed folders, moved files). Take the target board's pins file and the genuinely new driver or feature.\n\n### Rules\n\n1. **Use mainline's names and layout.** A fork's folder names may differ (for example `hc32f46x/` vs mainline `hc32f4/`). Check `ls Marlin/src/pins/` and follow it.\n2. **Board IDs must be verified against `boards.h`.** A fork's ID is often already taken in mainline. Use the next free ID in the right architecture range:\n\n       grep '#define BOARD_' Marlin/src/core/boards.h | awk '{print $3}' | sort -n\n\n   Keep the `boards.h` order the same as the `#elif MB()` order in `pins.h`.\n\n3. **Config defaults** go in the `Conditionals-*.h` layer. `SanityCheck.h` only validates currently-valid combinations.\n4. **Renamed or removed options** get an `#error` in `Marlin/src/inc/Changes.h` that points to the replacement.\n5. **Old G-codes and M-codes** that the port replaces should keep working as shims where reasonable.\n6. **EEPROM data** follows the struct pattern in `settings.cpp`, with a version bump.\n7. **Don't copy old APIs.** Fork code often uses names removed from mainline. Fix each compile error against current headers rather than adding compatibility macros.\n\n### New ExtUI display drivers\n\nSee skill marlin-extui-backends. In particular:\n\n- Add the driver folder to `ini/features.ini`. If it is missing, the whole driver is silently excluded and every ExtUI callback is \"undefined\".\n- Add the new LCD define to the lists in `Conditionals-2-LCD.h`, and add its `SanityCheck.h` entries.\n- Compare the fork's handlers with the current `ui_api.h` (renamed `onMediaMounted`, handlers with new argument types, newer handlers such as `onSurviveInKilled`).\n\n### Per-HAL notes\n\nRead `Marlin/src/HAL/<FAMILY>/AGENTS.md`. For example, a HAL may lack an EEPROM backend the fork relied on (`PersistentStore::*` undefined at link), or need a board-defined crystal frequency.\n\n## 5. Build and test\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_<NAME>\n    buildroot/bin/mftest -a -n1 -y\n    buildroot/bin/restore_configs\n\n- Build with the feature disabled and enabled.\n- Add a test config for new options (skill: marlin-build-test). A test config must satisfy the sanity checks, for example `NOZZLE_AS_PROBE` needs a zero probe offset, and bed leveling with a physical probe needs `Z_SAFE_HOMING`.\n- Say honestly in the PR what you built, and that no one has tested on hardware if that is so.\n\n## 6. Commit and PR\n\nSplit it: core pins and board first, display driver second, extra features after that (skill: marlin-pr-workflow). Credit the fork's author in the PR description.\n\n<!-- File: buildroot/skills/marlin-hal-work/SKILL.md (Tokens: ~1621 | Category: anthropic-skill) -->\n---\nname: marlin-hal-work\ndescription: Use when adding a board to an existing HAL, extending a HAL, creating a new HAL, or debugging HAL-specific code. Includes the board checklist, comparing HALs, and verifying framework APIs.\n---\n\n# Marlin HAL and Board Work\n\nEach HAL folder `Marlin/src/HAL/<FAMILY>/` has its own `AGENTS.md` with the build and test loop and the gotchas for that family. Read it first.\n\nThree levels of work, from most to least common:\n\n1. **Add a board** to an existing HAL.\n2. **Extend a HAL** with an MCU variant.\n3. **Create a new HAL** for a new architecture.\n\n## Compare, don't generalize\n\nDon't assume one HAL's behavior holds for another. Timers, ISR entry, serial buffering, and ADC differ a lot. To learn the contract, compare three representatives: a simple one (AVR), a mid one (LPC1768), and a complex one (STM32 or ESP32).\n\n    ls Marlin/src/HAL/*/                      # a file in 10+ HALs is probably required\n    head -80 Marlin/src/HAL/{AVR,LPC1768,STM32,RP2040}/timers.h\n    head -50 Marlin/src/HAL/{AVR,LPC1768,STM32,RP2040}/fastio.h\n\n`Marlin/src/HAL/shared/` holds what every HAL inherits (`Marduino.h`, `serial_ports.h`, `eeprom_api.h`, ...). `Marlin/src/HAL/platforms.h` is the single dispatch point: a wrong detection macro loads the wrong HAL, and the failure is confusing.\n\n## Adding a board\n\n| Step | File | Action |\n| --- | --- | --- |\n| Pins | `Marlin/src/pins/<arch>/pins_<BOARD>.h` | All `*_PIN` defines. Use a `_common.h` file when variants share a pinout |\n| Board ID | `Marlin/src/core/boards.h` | `#define BOARD_<NAME> <ID>` in the right architecture range, highest ID + 1 |\n| Routing | `Marlin/src/pins/pins.h` | `#elif MB(<NAME>)` in the same relative position as in `boards.h` |\n| Environment | `ini/<arch>.ini` | Add an `[env:...]` that `extends` a similar one |\n| Validation | `Marlin/src/pins/<arch>/env_validate.h` | Only if the family has a unique constraint |\n| CI test | `buildroot/tests/<env>/` | Optional |\n\nDo not edit `config.ini` for a board.\n\nConventions:\n\n- Letters between `pins_` and `.h` are UPPERCASE. The `_common` suffix is lowercase. Prefix the manufacturer's name if the board name isn't distinctive.\n- The `#define` order in `boards.h` must match the `#elif MB()` order in `pins.h`.\n- Set `BOARD_INFO_NAME` (and optionally `DEFAULT_MACHINE_NAME`) in the board's pins file.\n- Use `#ifndef` guards around pins that variants may override.\n- The EEPROM backend is chosen per board: most boards define `FLASH_EEPROM_EMULATION` or `SDCARD_EEPROM_EMULATION` in the pins file. If none is enabled you get an `undefined reference to PersistentStore::*` at link.\n- Link the schematic or source of the pin data in a comment, if there is one.\n\n### Verifying pin and UART assignments\n\nCheck open sources before saying a pin can't be verified without hardware. Cross-check at least two:\n\n1. Klipper configs (`Klipper3d/klipper/config/printer-<vendor>-<model>*.cfg`, `generic-<board>.cfg`). They list step/dir/enable/heater/fan pins, and the MCU serial pins in comments.\n2. Vendor or community Marlin forks: `pins_*.h`, the variant's `variant.h` and `PeripheralPins.c`, and the ini env's `build_flags`.\n3. Elimination: a UART has only a few alternate-function pin pairs. If the others are used by steppers or heaters, the one left is the answer.\n\nSTM32duino uses `PIN_SERIALn_RX/TX` if defined. Otherwise it takes the first matching entry in the variant's pin map. A board whose UART is on a later pair needs `-DPIN_SERIALn_RX=...` and `-DPIN_SERIALn_TX=...` in its env.\n\n### Validate with mftest\n\n    buildroot/bin/opt_set MOTHERBOARD BOARD_<NAME>\n    buildroot/bin/mftest -a -n1 -y\n\nRaw `pio run -e <env>` reads whatever `Configuration.h` was last generated, and can report a phantom `Build environment is incompatible with BOARD_...`. Trust `mftest` and don't chase a pio-only \"incompatible\" error. A passing build only shows the files are structurally sound. Say so in the PR if no one has tested the board.\n\n## Creating a new HAL\n\nThe minimum set that must exist to compile:\n\n- A branch in `platforms.h`\n- `HAL.h` and `HAL.cpp` (the `MarlinHAL` class)\n- `fastio.h`\n- `timers.h` and `timers.cpp`\n- `MarlinSerial.h` and `MarlinSerial.cpp`\n- `inc/Conditionals-2-LCD.h` and `inc/SanityCheck.h`, plus the other files in `inc/`, which can be empty (`#pragma once`) but must exist\n\nUse a recent HAL such as HC32 as the layout reference. Everything else fills in as features are enabled.\n\n## Verify against the real framework\n\nCode written without the vendor framework in view tends to invent names borrowed from another platform (for example `GPIO_TypeDef`, `BSRR`, `Pin_TypeDef`). The installed framework (`~/.platformio/packages/framework-*`) is ground truth: grep it before writing or \"fixing\" any peripheral symbol. A symbol that isn't there is a HAL bug, not a missing dependency.\n\n## Pitfalls\n\n- A HAL header missing `#include \"../../inc/MarlinConfig.h\"` makes shared files report `FSTR_P`, `PGM_P`, `millis_t`, or `pin_t` undefined. Fix the one include, not the cascade. Files in `HAL/<ARCH>/<sub>/` need `../../../inc/MarlinConfig.h`.\n- A timer ISR macro with no vector or NVIC wiring compiles but never fires.\n- `MF_TIMER_STEP`, `MF_TIMER_TEMP`, and `MF_TIMER_PWM` must not collide with each other or with platform-reserved timers.\n- `CRITICAL_SECTION_START/END` must nest (save and restore the previous interrupt state). Don't use bare `__disable_irq()` / `__enable_irq()`.\n- `pin_t` must match the platform's GPIO numbering.\n- `SERIAL_PORT` numbering differs between HALs (AVR 0-3, STM32 and RP2040 -1/0/1).\n- A serial adapter class must subclass the real core serial class. Forward-declaring it gives \"incomplete type\" errors.\n- Some cores lack the bare `Serial` global or `spiBegin()`, which Marlin code calls.\n- Vendored framework files may use CRLF. Some editors and tools turn an escape such as `\"\\r\\n\"` into a real control character, so use hex constants like `0x0D`.\n- Don't remove or break the AVR HAL. It is the reference implementation.\n- Some boards must keep the base AVR flash and RAM budget. Don't let the base build grow.\n\n<!-- File: buildroot/skills/marlin-pr-workflow/SKILL.md (Tokens: ~642 | Category: anthropic-skill) -->\n---\nname: marlin-pr-workflow\ndescription: Use when preparing, splitting, or submitting a Marlin Pull Request. Covers PR size, branch, commit messages, rebase, and the PR template.\n---\n\n# Marlin Pull Request Workflow\n\nPolicy background: `.github/contributing.md` (AI-Assisted Contributions) and `AGENTS.md` section 11.\n\n## 1. Keep it small\n\nA PR does one thing and builds on its own. If your working tree holds more than one logical change, or a reviewer would struggle to follow it, split it into two or more PRs:\n\n- refactor first, behavior change second\n- core logic first, LCD/UI support second\n- one HAL, board family, or pin file group per PR\n- unrelated fixes in their own PRs\n\nTo split: create a new branch from `upstream/bugfix-2.1.x` for each piece and bring over only its changes (`git checkout <work-branch> -- <paths>` or `git cherry-pick -n`, then commit selectively with `git add -p`). Build each branch separately. State any dependency between the PRs in their descriptions.\n\n## 2. Branch\n\n    git fetch upstream\n    git checkout -b my_change upstream/bugfix-2.1.x\n\nPRs target `bugfix-2.1.x`, never a release branch.\n\n## 3. Before committing\n\n- Format only your changed files (skill: marlin-code-style).\n- Build the affected environments (skill: marlin-build-test).\n- Restore the config files after testing (`buildroot/bin/restore_configs` discards uncommitted edits to them, so commit any real config changes first). Don't commit test-only config edits.\n- Add tests for any new feature or option: a `buildroot/tests/<env>/config-NN.ini` build test, and a unit test under `Marlin/tests` when the logic can run without hardware (skill: marlin-build-test).\n- Review `git diff` for unrelated changes.\n\n## 4. Commit\n\n- Imperative mood, 72 characters or fewer on the first line, gitmoji-style emoji prefix like recent `git log` entries (🐛 fix, ✨ feature, ⚡️ performance, 🔧 config, 🩹 minor fix, 🧑‍💻 developer, 🚸 UX).\n- One logical change per commit. Squash fixups.\n\n## 5. Keep current\n\nRebase, don't merge:\n\n    git fetch upstream\n    git rebase upstream/bugfix-2.1.x\n    git push -f\n\n## 6. Open the PR\n\nFill in every section of `.github/pull_request_template.md`. No issue numbers in the title. List what you built and tested, and on what hardware if any. Attach a Configurations ZIP if reviewers need one to test.\n\nThe `mf*` helper scripts (`mfnew`, `mfrb`, `mfpr`, ...) described at https://marlinfw.org/docs/development/git_scripts.html are optional conveniences for these steps.\n\n<!-- File: buildroot/skills/marlin-solving-bugs/SKILL.md (Tokens: ~1371 | Category: anthropic-skill) -->\n---\nname: marlin-solving-bugs\ndescription: Use when investigating and fixing a Marlin bug report, from reproducing it to a verified patch. Covers triage, finding the code path, isolating the cause, a minimal root-cause fix, and verification.\n---\n\n# Fix a Marlin Bug\n\nFind the root cause before you write any fix. Marlin has many HALs and a huge number of configuration combinations, and there is no hardware-in-the-loop CI, so the evidence you gather matters more than the size of the patch.\n\n## 1. Triage the report\n\n- Get the exact Marlin version or commit, the board, and `Configuration.h` / `Configuration_adv.h` (a `marlin_config.json` or a Configurations ZIP is best).\n- Find the first version that failed, and the last that worked. If the reporter can test, ask for a bisect (`git bisect`) between those points.\n- Check whether `bugfix-2.1.x` still has the bug. Fixes land there first, and old reports are often already fixed.\n- Files move between releases. Search for the current location of any path in an old report: `ultralcd.*` became `marlinui.*`, and `Conditionals_post.h` became `Conditionals-5-post.h`.\n\n## 2. Find the code path\n\n    git grep -n \"OPTION_NAME\" -- Marlin/src            # who uses an option\n    git log --oneline -20 -- Marlin/src/module/motion.cpp\n    git log -S\"suspicious_string\" --oneline -- Marlin/src\n    git log --oneline 2.1.2..2.1.3 -- Marlin/src/module/probe.cpp\n\n- G-code handlers are in `Marlin/src/gcode/`, with one file per command or small group.\n- Trace which `#if` paths the reporter's config enables. A bug often appears only for one combination of options.\n- A regression usually has a specific commit. Read its diff and its PR discussion to learn what it was meant to do.\n\n## 3. Isolate the cause\n\n- Reproduce on the smallest config you can, using `buildroot/bin/opt_*` to set only the needed options (skill: marlin-config-options).\n- Test as much as possible in the native simulator ([MarlinSimUI](https://github.com/MarlinFirmware/MarlinSimUI)) or a unit test, so you can run and re-run the scenario.\n- Change one thing at a time and keep notes. A temporary `SERIAL_ECHOLNPGM` can help, but remove every debug statement before committing.\n- State your hypothesis and the evidence before coding the fix. If you can, show that reverting the suspect change removes the symptom.\n\n## 4. Fix the root cause\n\n- Fix the wrong logic itself. Don't add workarounds elsewhere that hide the symptom.\n- Make the smallest diff that does it. No reformatting, no unrelated cleanup (skill: marlin-code-style).\n- Think about every configuration the changed code is compiled in. Build with the relevant options both on and off.\n\nCommon causes:\n\n| Symptom | Look for |\n| --- | --- |\n| Wrong distance or speed | mm vs steps vs mm/s units; missing conversion |\n| Works with one option, breaks with another | A missing `ENABLED()` guard, or the wrong layer in `Conditionals-*.h` |\n| Wrong axis | `X_AXIS` used where `Z_AXIS` was meant; macro for the wrong axis |\n| Fails only on AVR | Integer overflow (use `uint32_t`), `double`, RAM and flash limits |\n| Fails only on one board | HAL differences: timers, serial buffering, pin multiplexing |\n| Position drift after an event | A reset of `current_position` or the planner position without `sync_plan_position()` |\n| Lost settings after upgrade | A `settings.cpp` layout change without a version bump |\n\n## 5. Verify\n\n- Build with `mftest` (skill: marlin-build-test). At least one AVR (tight flash) and one 32-bit environment for common code.\n- Run the reporter's scenario again, not just a build. A passing build is not proof the bug is fixed.\n- If the logic is testable without hardware, add a unit test that fails before your fix and passes after.\n- If you add a config interaction, add a check to `Marlin/src/inc/SanityCheck.h`.\n\n## 6. Submit\n\nFollow skill marlin-pr-workflow. In the PR description, say what the root cause was, how you reproduced it, and what you tested (including on what hardware, if any). Reference the issue in the description, not the title. Don't claim the issue is closed until the reporter or another tester confirms it on hardware.\n\n## Pitfalls\n\n- **Debug code left in.** `#define DEBUG_OUT 1`, `DEBUG_ECHOLNPGM`, and temporary prints must not be in the final diff. Check with `git diff | grep -n DEBUG_`.\n- **Stale `case` labels.** After an enum value is removed, a `case REMOVED_VALUE:` can survive a rebase. Grep for removed names when a PR touches enums.\n- **Shared string buffers.** The `numtostr` family (`ftostr*`, `i16tostr*`) writes into one shared buffer. Never pass one function's result into another, and don't hold a pointer across calls.\n- **Variables declared outside the guard of their only uses** cause `-Wunused-variable`, and CI treats warnings as errors. Put the declaration inside the same guard.\n- **A function whose signature uses a type from a guarded header** needs its definition guarded the same way as its declaration.\n- **Verify a premise before relying on it.** For example, `IS_SCARA` also covers TPARA, so grep the macro definition before saying a path doesn't apply to a variant.\n\n## Reading a failed CI run\n\n    gh run view <run_id> --repo MarlinFirmware/Marlin --json jobs -q '.jobs[] | select(.conclusion==\"failure\") | [.databaseId, .name] | @tsv'\n    gh run view <run_id> --job <job_id> --log | grep -E \"error:|FAILED|undefined\" | head -30\n\nA job that failed in under 30 seconds is nearly always a compile or sanity-check error. Longer ones are often timeouts.\n\n<!-- File: buildroot/skills/marlin-unit-tests/SKILL.md (Tokens: ~526 | Category: anthropic-skill) -->\n---\nname: marlin-unit-tests\ndescription: Use when writing or running Marlin's native unit tests (make unit-test-*), or when a native unit-test build fails locally, especially on macOS.\n---\n\n# Marlin Native Unit Tests\n\nBackground: `test/README.md`. For adding a test to a feature PR, see also the unit-test section of skill marlin-build-test.\n\n## Layout\n\n- Test sources: `Marlin/tests/<area>/test_<name>.cpp` (Unity, `MARLIN_TEST(group, name)`).\n- Test configurations: `test/NNN-name.ini`. Each one builds a separate test binary with a different Marlin configuration. Suites are discovered from this folder by `buildroot/share/PlatformIO/scripts/collect-code-tests.py`.\n- The PlatformIO environment is `linux_native_test` in `ini/native.ini`. It is built on the Linux native HAL, with `-Werror`.\n\n## Run\n\n    make unit-test-all-local                              # all configs\n    make unit-test-single-local UNIT_TEST_CONFIG=default  # one config (test/001-default.ini)\n    make unit-test-all-local-docker                       # same, in a container\n\n## Write one\n\n1. Copy the license header and macros from an existing test, such as `Marlin/tests/feature/test_runout.cpp`.\n2. Wrap the file in `#if ENABLED(FEATURE)` so it compiles only for configs that enable the feature.\n3. If no `test/*.ini` enables the feature, add one with `motherboard = BOARD_SIMULATED` and only the options required to pass the sanity checks.\n4. Test logic, not hardware: parsing, math, conversions, state machines. Anything needing real pins or timers isn't a good candidate.\n\n## macOS\n\nOn macOS `/usr/bin/gcc` is Clang, which cannot build Marlin. Install GCC with MacPorts or Homebrew, then:\n\n    buildroot/bin/mac_gcc macports      # or: homebrew   (apple resets to the system compiler)\n    which gcc                           # should now be the real GCC\n\nIf the native build fails on macOS, first check the compiler diagnostics and which `gcc` you got. Don't assume the failure is a bug in the test wiring.\n\n## Pitfalls\n\n- Test files are compiled with `-Werror`. An unused variable or sign-compare warning fails the run.\n- Don't work around a compiler warning with duplicate includes or duplicate keys in the INI.\n- Run the test configs one at a time. They share the `.pio` build directory.\n\n<!-- File: buildroot/skills/README.md (Tokens: ~520 | Category: anthropic-skill) -->\n# Marlin Contributor Skills\n\nStep-by-step procedures for working on Marlin, written for AI coding agents and equally useful as human cheat sheets. Each skill is a folder containing a `SKILL.md` with YAML front matter (`name`, `description`) followed by instructions. They refer to the scripts in `buildroot/bin` and the `Makefile` so the procedures stay accurate as the scripts evolve.\n\nAll commands assume the current directory is the repository root.\n\n| Skill | Purpose |\n| --- | --- |\n| [marlin-build-test](marlin-build-test/SKILL.md) | Build a board environment, apply test configs, run CI-style tests |\n| [marlin-config-options](marlin-config-options/SKILL.md) | Find, enable, disable, and set configuration options from the command line |\n| [marlin-code-style](marlin-code-style/SKILL.md) | Follow the Marlin coding standards and indentation; leave existing code unformatted |\n| [marlin-pr-workflow](marlin-pr-workflow/SKILL.md) | Prepare a focused, reviewable Pull Request |\n| [marlin-solving-bugs](marlin-solving-bugs/SKILL.md) | Investigate, isolate, fix, and verify a bug report |\n| [marlin-extui-backends](marlin-extui-backends/SKILL.md) | Add or change ExtUI display backends and the ExtUI API |\n| [marlin-unit-tests](marlin-unit-tests/SKILL.md) | Write and run native unit tests; macOS GCC setup |\n| [marlin-codebase-navigation](marlin-codebase-navigation/SKILL.md) | Find definitions, uses, and flows in the source with git and grep |\n| [marlin-hal-work](marlin-hal-work/SKILL.md) | Add a board, extend a HAL, or create a new HAL |\n| [marlin-fork-harvesting](marlin-fork-harvesting/SKILL.md) | Evaluate a Marlin fork and port its features cleanly |\n\n## Adding a skill\n\n- Make a folder `buildroot/skills/<skill-name>/` with a `SKILL.md`.\n- Start `description` with when to use the skill. Keep the skill short and procedural.\n- Refer to scripts in `buildroot/bin` instead of duplicating their logic, and verify every command you document.\n- Skills are for all contributors. Don't include personal paths or maintainer-only procedures.\n- Add the skill to the table above.\n</yakaai_skills>"}