diff --git a/.editorconfig b/.editorconfig
index a0fa3eff17..57a5b2fb5e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,16 +4,20 @@ root = true
 [{*.patch,syntax_test_*}]
 trim_trailing_whitespace = false
 
-[{*.c,*.cpp,*.h}]
+[{*.c,*.cpp,*.h,*.ino}]
 charset = utf-8
 
-[{*.c,*.cpp,*.h,Makefile}]
+[{*.c,*.cpp,*.h,*.ino,Makefile}]
 trim_trailing_whitespace = true
 insert_final_newline = true
 end_of_line = lf
 indent_style = space
 indent_size = 2
 
-[{*.py,*.conf,*.sublime-project}]
+[{*.py}]
+indent_style = space
+indent_size = 4
+
+[{*.conf,*.sublime-project}]
 indent_style = tab
 indent_size = 4
diff --git a/.gitattributes b/.gitattributes
index 2588229e05..83897cba6e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -17,3 +17,5 @@
 *.png binary
 *.jpg binary
 *.fon binary
+*.bin binary
+*.woff binary
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 48beca2c2d..0000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,43 +0,0 @@
----
-name: Bug report
-about: Report a bug in Marlin
-title: "[BUG] (short description)"
-labels: ''
-assignees: ''
-
----
-
-
-
-### Bug Description
-
-
-
-### My Configurations
-
-**Required:** Please include a ZIP file containing your `Configuration.h` and `Configuration_adv.h` files.
-
-### Steps to Reproduce
-
-
-
-1. [First Step]
-2. [Second Step]
-3. [and so on...]
-
-**Expected behavior:** [What you expect to happen]
-
-**Actual behavior:** [What actually happens]
-
-#### Additional Information
-
-* Provide pictures or links to videos that clearly demonstrate the issue.
-* See [How Can I Contribute](#how-can-i-contribute) for additional guidelines.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
deleted file mode 100644
index 4b283d2600..0000000000
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-blank_issues_enabled: false
-contact_links:
-  - name: Marlin Documentation
-    url: http://marlinfw.org/
-    about: Lots of documentation on installing and using Marlin.
-  - name: MarlinFirmware Facebook group
-    url: https://www.facebook.com/groups/1049718498464482
-    about: Please ask and answer questions here.
-  - name: Marlin on Discord
-    url: https://discord.gg/n5NJ59y
-    about: Join the Discord server for support and discussion.
-  - name: Marlin Discussion Forum
-    url: http://forums.reprap.org/list.php?415
-    about: A searchable web forum hosted by RepRap dot org.
-  - name: Marlin Videos on YouTube
-    url: https://www.youtube.com/results?search_query=marlin+firmware
-    about: Tutorials and more from Marlin users all around the world. Great for new users!
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 24de8d0141..0000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-name: Feature request
-about: Request a Feature
-title: "[FR] (feature request title)"
-labels: 'T: Feature Request'
-assignees: ''
-
----
-
-
-
-### Description
-
-
-
-### Feature Workflow
-
-
-
-1. [First Action]
-2. [Second Action]
-3. [and so on...]
-
-#### Additional Information
-
-* Provide pictures or links that demonstrate a similar feature or concept.
-* See [How Can I Contribute](#how-can-i-contribute) for additional guidelines.
diff --git a/.github/contributing.md b/.github/contributing.md
index 6bc7b5a005..ef1726366a 100644
--- a/.github/contributing.md
+++ b/.github/contributing.md
@@ -34,8 +34,11 @@ This project and everyone participating in it is governed by the [Marlin Code of
 
 We have a Message Board and a Facebook group where our knowledgable user community can provide helpful advice if you have questions.
 
-* [Marlin RepRap forum](https://reprap.org/forum/list.php?415)
-* [MarlinFirmware on Facebook](https://www.facebook.com/groups/1049718498464482/)
+- [Marlin Documentation](https://marlinfw.org) - Official Marlin documentation
+- Facebook Group ["Marlin Firmware"](https://www.facebook.com/groups/1049718498464482/)
+- RepRap.org [Marlin Forum](https://forums.reprap.org/list.php?415)
+- Facebook Group ["Marlin Firmware for 3D Printers"](https://www.facebook.com/groups/3Dtechtalk/)
+- [Marlin Configuration](https://www.youtube.com/results?search_query=marlin+configuration) on YouTube
 
 If chat is more your speed, you can join the MarlinFirmware Discord server:
 
@@ -50,13 +53,13 @@ If chat is more your speed, you can join the MarlinFirmware Discord server:
 
 This section guides you through submitting a Bug Report for Marlin. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
 
-Before creating a Bug Report, please test the "nightly" development branch, as you might find out that you don't need to create one. When you are creating a Bug Report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](issue_template.md), the information it asks for helps us resolve issues faster.
+Before creating a Bug Report, please test the "nightly" development branch, as you might find out that you don't need to create one. When you are creating a Bug Report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](ISSUE_TEMPLATE/bug_report.yml), the information it asks for helps us resolve issues faster.
 
 > **Note:** Regressions can happen. If you find a **Closed** issue that seems like your issue, go ahead and open a new issue and include a link to the original issue in the body of your new one. All you need to create a link is the issue number, preceded by #. For example, #8888.
 
 #### How Do I Submit A (Good) Bug Report?
 
-Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Use the New Issue button to create an issue and provide the following information by filling in [the template](issue_template.md).
+Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Use the New Issue button to create an issue and provide the following information by filling in [the template](ISSUE_TEMPLATE/bug_report.yml).
 
 Explain the problem and include additional details to help maintainers reproduce the problem:
 
@@ -88,12 +91,12 @@ Include details about your configuration and environment:
 
 This section guides you through submitting a suggestion for Marlin, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions.
 
-Before creating a suggestion, please check [this list](#before-submitting-a-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). Fill in [the template](issue_template.md), including the steps that you imagine you would take if the feature you're requesting existed.
+Before creating a suggestion, please check [this list](https://github.com/MarlinFirmware/Marlin/issues?q=is%3Aopen+is%3Aissue+label%3A%22T%3A+Feature+Request%22) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-feature-request). Fill in [the template](ISSUE_TEMPLATE/feature_request.yml), including the steps that you imagine you would take if the feature you're requesting existed.
 
 #### Before Submitting a Feature Request
 
 * **Check the [Marlin website](https://marlinfw.org/)** for tips — you might discover that the feature is already included. Most importantly, check if you're using [the latest version of Marlin](https://github.com/MarlinFirmware/Marlin/releases) and if you can get the desired behavior by changing [Marlin's config settings](https://marlinfw.org/docs/configuration/configuration.html).
-* **Perform a [cursory search](https://github.com/MarlinFirmware/Marlin/issues?q=is%3Aissue)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
+* **Perform a [cursory search](https://github.com/MarlinFirmware/Marlin/issues?q=is%3Aopen+is%3Aissue+label%3A%22T%3A+Feature+Request%22)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
 
 #### How Do I Submit A (Good) Feature Request?
 
@@ -116,7 +119,7 @@ Unsure where to begin contributing to Marlin? You can start by looking through t
 
 ### Pull Requests
 
-Pull Requests should always be targeted to working branches (e.g., `bugfix-1.1.x` and/or `bugfix-2.0.x`) and never to release branches (e.g., `1.1.x`). If this is your first Pull Request, please read our [Guide to Pull Requests](https://marlinfw.org/docs/development/getting_started_pull_requests.html) and Github's [Pull Request](https://help.github.com/articles/creating-a-pull-request/) documentation.
+Pull Requests should always be targeted to working branches (e.g., `bugfix-2.1.x` and/or `bugfix-1.1.x`) and never to release branches (e.g., `2.0.x` and/or `1.1.x`). If this is your first Pull Request, please read our [Guide to Pull Requests](https://marlinfw.org/docs/development/getting_started_pull_requests.html) and Github's [Pull Request](https://help.github.com/articles/creating-a-pull-request/) documentation.
 
 * Fill in [the required template](pull_request_template.md).
 * Don't include issue numbers in the PR title.
diff --git a/.github/issue_template.md b/.github/issue_template.md
deleted file mode 100644
index a211ca5e27..0000000000
--- a/.github/issue_template.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# NO SUPPORT REQUESTS PLEASE
-
-Do you want to ask a question? Are you looking for support? Please don't post here. Support Requests posted here will be automatically closed!
-
-Instead use one of the following options:
-
-- The Marlin Firmware forum at https://reprap.org/forum/list.php?415
-- The MarlinFirmware Facebook Group at https://www.facebook.com/groups/1049718498464482/
-- The MarlinFirmware Discord Server at https://discord.gg/n5NJ59y.
-
-Before filing an issue be sure to test the latest "bugfix" branch to see whether the issue is already addressed.
diff --git a/.github/lock.yml b/.github/lock.yml
deleted file mode 100644
index c5ceff66b0..0000000000
--- a/.github/lock.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app
-#
-
-# Number of days of inactivity before a closed issue or pull request is locked
-daysUntilLock: 60
-
-# Skip issues and pull requests created before a given timestamp. Timestamp must
-# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
-skipCreatedBefore: false
-
-# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
-exemptLabels: [ 'no-locking' ]
-
-# Label to add before locking, such as `outdated`. Set to `false` to disable
-lockLabel: false
-
-# Comment to post before locking. Set to `false` to disable
-lockComment: >
-  This thread has been automatically locked since there has not been
-  any recent activity after it was closed. Please open a new issue for
-  related bugs.
-
-# Assign `resolved` as the reason for locking. Set to `false` to disable
-setLockReason: true
-
-# Limit to only `issues` or `pulls`
-# only: issues
-
-# Optionally, specify configuration settings just for `issues` or `pulls`
-# issues:
-#   exemptLabels:
-#     - help-wanted
-#   lockLabel: outdated
-
-# pulls:
-#   daysUntilLock: 30
-
-# Repository to extend settings from
-# _extends: repo
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index d82fb0f9e3..cd5158b3ce 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,23 +1,33 @@
-### Requirements
+
 
 ### Description
 
 
 
+### Requirements
+
+
+
 ### Benefits
 
-
+
 
 ### Configurations
 
-
+
 
 ### Related Issues
 
-
+
diff --git a/.github/workflows/bump-date.yml b/.github/workflows/bump-date.yml
deleted file mode 100644
index 54902da8c9..0000000000
--- a/.github/workflows/bump-date.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# bump-date.yml
-# Bump the distribution date once per day
-#
-
-name: Bump Distribution Date
-
-on:
-  schedule:
-    - cron: '0 0 * * *'
-
-jobs:
-  bump_date:
-    name: Bump Distribution Date
-    if: github.repository == 'MarlinFirmware/Marlin'
-
-    runs-on: ubuntu-latest
-
-    steps:
-
-    - name: Check out bugfix-2.0.x
-      uses: actions/checkout@v2
-      with:
-        ref: bugfix-2.0.x
-
-    - name: Bump Distribution Date
-      run: |
-        # Inline Bump Script
-        DIST=$( date +"%Y-%m-%d" )
-        eval "sed -E -i 's/(#define +STRING_DISTRIBUTION_DATE) .*$/\1 \"$DIST\"/g' Marlin/src/inc/Version.h" && \
-        git config user.name "${GITHUB_ACTOR}" && \
-        git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" && \
-        git add . && \
-        git commit -m "[cron] Bump distribution date ($DIST)" && \
-        git push
diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml
deleted file mode 100644
index aa4a2c59c9..0000000000
--- a/.github/workflows/check-pr.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# check-pr.yml
-# Close PRs directed at release branches
-#
-
-name: PR Bad Target
-
-on:
-  pull_request:
-    branches:
-    - 1.0.x
-    - 1.1.x
-    - 2.0.x
-
-jobs:
-  bad_target:
-    name: PR Bad Target
-    if: github.repository == 'MarlinFirmware/Marlin'
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: peter-evans/close-pull@v1
-      with:
-        delete-branch: false
-        comment: >
-          Thanks for your contribution! Unfortunately we can't accept PRs directed at release branches. We make patches to the bugfix branches and only later do we push them out as releases.
-
-          Please redo this PR starting with the `bugfix-2.0.x` branch and be careful to target `bugfix-2.0.x` when resubmitting the PR.
-
-          It may help to set your fork's default branch to `bugfix-2.0.x`.
-
-          See [this page](http://marlinfw.org/docs/development/getting_started_pull_requests.html) for full instructions.
diff --git a/.github/workflows/close-stale.yml b/.github/workflows/close-stale.yml
deleted file mode 100644
index 89d5d65351..0000000000
--- a/.github/workflows/close-stale.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# close-stale.yml
-# Close open issues after a period of inactivity
-#
-
-name: Close Stale Issues
-
-on:
-  schedule:
-  - cron: "22 1 * * *"
-
-jobs:
-  stale:
-    name: Close Stale Issues
-    if: github.repository == 'MarlinFirmware/Marlin'
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/stale@v3
-      with:
-        repo-token: ${{ secrets.GITHUB_TOKEN }}
-        stale-issue-message: 'This issue has had no activity in the last 30 days. Please add a reply if you want to keep this issue active, otherwise it will be automatically closed within 7 days.'
-        days-before-stale: 30
-        days-before-close: 7
-        stale-issue-label: 'stale-closing-soon'
-        exempt-issue-labels: 'T: Feature Request'
diff --git a/.github/workflows/lock-closed.yml b/.github/workflows/lock-closed.yml
deleted file mode 100644
index 8cdcd7a836..0000000000
--- a/.github/workflows/lock-closed.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# lock-closed.yml
-# Lock closed issues after a period of inactivity
-#
-
-name: Lock Closed Issues
-
-on:
-  schedule:
-    - cron: '0 1/13 * * *'
-
-jobs:
-  lock:
-    name: Lock Closed Issues
-    if: github.repository == 'MarlinFirmware/Marlin'
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: dessant/lock-threads@v2
-      with:
-        github-token: ${{ github.token }}
-        process-only: 'issues'
-        issue-lock-inactive-days: '60'
-        issue-exclude-created-before: ''
-        issue-exclude-labels: 'no-locking'
-        issue-lock-labels: ''
-        issue-lock-comment: >
-          This issue has been automatically locked since there
-          has not been any recent activity after it was closed.
-          Please open a new issue for related bugs.
-        issue-lock-reason: ''
diff --git a/.github/workflows/test-builds.yml b/.github/workflows/test-builds.yml
deleted file mode 100644
index b228783799..0000000000
--- a/.github/workflows/test-builds.yml
+++ /dev/null
@@ -1,123 +0,0 @@
-#
-# test-builds.yml
-# Do test builds to catch compile errors
-#
-
-name: CI
-
-on:
-  pull_request:
-    branches:
-    - bugfix-2.0.x
-    paths-ignore:
-    - config/**
-    - data/**
-    - docs/**
-    - '**/*.md'
-  push:
-    branches:
-    - bugfix-2.0.x
-    paths-ignore:
-    - config/**
-    - data/**
-    - docs/**
-    - '**/*.md'
-
-jobs:
-  test_builds:
-    name: Run All Tests
-    if: github.repository == 'MarlinFirmware/Marlin'
-
-    runs-on: ubuntu-latest
-
-    strategy:
-      matrix:
-        test-platform:
-        # Base Environments
-
-        - DUE
-        - esp32
-        - linux_native
-        - mega2560
-        - teensy31
-        - teensy35
-        - teensy41
-        - SAMD51_grandcentral_m4
-
-        # Extended AVR Environments
-
-        - FYSETC_F6_13
-        - mega1280
-        - rambo
-        - sanguino1284p
-        - sanguino644p
-
-        # Extended STM32 Environments
-
-        - STM32F103RC_btt
-        - STM32F103RC_btt_USB
-        - STM32F103RE_btt
-        - STM32F103RE_btt_USB
-        - STM32F103RC_fysetc
-        - STM32F103RC_meeb
-        - jgaurora_a5s_a1
-        - STM32F103VE_longer
-        - STM32F407VE_black
-        - STM32F401VE_STEVAL
-        - BIGTREE_BTT002
-        - BIGTREE_SKR_PRO
-        - BIGTREE_GTR_V1_0
-        - mks_robin
-        - mks_robin_stm32
-        - ARMED
-        - FYSETC_S6
-        - STM32F070CB_malyan
-        - STM32F070RB_malyan
-        - malyan_M300
-        - mks_robin_lite
-        - FLYF407ZG
-        - rumba32
-        - mks_robin_pro
-        - STM32F103RET6_creality
-        - LERDGEX
-        - mks_robin_nano35
-
-        # Put lengthy tests last
-
-        - LPC1768
-        - LPC1769
-
-        # STM32 with non-STM framework. both broken for now. they should use HAL_STM32 which is working.
-
-        #- STM32F4
-        #- STM32F7
-
-        # Non-working environment tests
-        #- at90usb1286_cdc
-        #- at90usb1286_dfu
-        #- STM32F103CB_malyan
-        #- mks_robin_mini
-
-    steps:
-
-    - name: Select Python 3.7
-      uses: actions/setup-python@v1
-      with:
-        python-version: '3.7' # Version range or exact version of a Python version to use, using semvers version range syntax.
-        architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
-
-    - name: Install PlatformIO
-      run: |
-        pip install -U https://github.com/platformio/platformio-core/archive/develop.zip
-        platformio update
-
-    - name: Check out the PR
-      uses: actions/checkout@v2
-
-    - name: Run ${{ matrix.test-platform }} Tests
-      run: |
-        # Inline tests script
-        chmod +x buildroot/bin/*
-        chmod +x buildroot/tests/*
-        export PATH=./buildroot/bin/:./buildroot/tests/:${PATH}
-        run_tests . ${{ matrix.test-platform }}
diff --git a/.github/workflows/unlock-reopened.yml b/.github/workflows/unlock-reopened.yml
deleted file mode 100644
index 614ef3fab2..0000000000
--- a/.github/workflows/unlock-reopened.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# unlock-reopened.yml
-# Unlock an issue whenever it is re-opened
-#
-
-name: "Unlock reopened issue"
-
-on:
-  issues:
-    types: [reopened]
-
-jobs:
-  unlock:
-    name: Unlock Reopened
-    if: github.repository == 'MarlinFirmware/Marlin'
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: OSDKDev/unlock-issues@v1.1
-      with:
-        repo-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.gitignore b/.gitignore
index c163d339df..09db344257 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,15 +19,19 @@
 # along with this program.  If not, see .
 #
 
-# Our automatic versioning scheme generates the following file
-# NEVER put it in the repository
+# Generated files
 _Version.h
+bdf2u8g
+marlin_config.json
+mczip.h
+*.gen
+*.sublime-workspace
 
 #
 # OS
 #
 applet/
-*.DS_Store
+.DS_Store
 
 #
 # Misc
@@ -37,7 +41,6 @@ applet/
 *.rej
 *.bak
 *.idea
-*.s
 *.i
 *.ii
 *.swp
@@ -77,7 +80,6 @@ tags
 *.out
 *.app
 
-
 #
 # C
 #
@@ -123,33 +125,10 @@ tags
 .gcc-flags.json
 /lib/
 
-# Workaround for Deviot+platformio quirks
-Marlin/lib
-Marlin/platformio.ini
-Marlin/*/platformio.ini
-Marlin/*/*/platformio.ini
-Marlin/*/*/*/platformio.ini
-Marlin/*/*/*/*/platformio.ini
-Marlin/.travis.yml
-Marlin/*/.travis.yml
-Marlin/*/*/.travis.yml
-Marlin/*/*/*/.travis.yml
-Marlin/*/*/*/*/.travis.yml
-Marlin/.gitignore
-Marlin/*/.gitignore
-Marlin/*/*/.gitignore
-Marlin/*/*/*/.gitignore
-Marlin/*/*/*/*/.gitignore
-Marlin/readme.txt
-Marlin/*/readme.txt
-Marlin/*/*/readme.txt
-Marlin/*/*/*/readme.txt
-Marlin/*/*/*/*/readme.txt
-
 # Secure Credentials
 Configuration_Secure.h
 
-#Visual Studio
+# Visual Studio
 *.sln
 *.vcxproj
 *.vcxproj.user
@@ -160,27 +139,34 @@ __vm/
 .vs/
 vc-fileutils.settings
 
-#Visual Studio Code
-.vscode
-.vscode/.browse.c_cpp.db*
-.vscode/c_cpp_properties.json
-.vscode/launch.json
-.vscode/*.db
+# Visual Studio Code
+.vscode/*
+!.vscode/extensions.json
+
+#Simulation
+imgui.ini
+eeprom.dat
+spi_flash.bin
+fs.img
 
 #cmake
 CMakeLists.txt
 src/CMakeLists.txt
 CMakeListsPrivate.txt
+build/
 
-#CLion
+# CLion
 cmake-build-*
 
-#Eclipse
+# Eclipse
 .project
 .cproject
 .pydevproject
 .settings
 .classpath
 
-#Python
+# Python
 __pycache__
+
+# IOLogger logs
+*_log.csv
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000000..f495d14f53
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,11 @@
+{
+    // See http://go.microsoft.com/fwlink/?LinkId=827846
+    // for the documentation about the extensions.json format
+    "recommendations": [
+        "marlinfirmware.auto-build",
+        "platformio.platformio-ide"
+    ],
+    "unwantedRecommendations": [
+        "ms-vscode.cpptools-extension-pack"
+    ]
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000..d0495dc7d8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,52 @@
+help:
+	@echo "Tasks for local development:"
+	@echo "* tests-single-ci:             Run a single test from inside the CI"
+	@echo "* tests-single-local:          Run a single test locally"
+	@echo "* tests-single-local-docker:   Run a single test locally, using docker-compose"
+	@echo "* tests-all-local:             Run all tests locally"
+	@echo "* tests-all-local-docker:      Run all tests locally, using docker-compose"
+	@echo "* setup-local-docker:          Setup local docker-compose"
+	@echo ""
+	@echo "Options for testing:"
+	@echo "  TEST_TARGET          Set when running tests-single-*, to select the"
+	@echo "                       test. If you set it to ALL it will run all "
+	@echo "                       tests, but some of them are broken: use "
+	@echo "                       tests-all-* instead to run only the ones that "
+	@echo "                       run on GitHub CI"
+	@echo "  ONLY_TEST            Limit tests to only those that contain this, or"
+	@echo "                       the index of the test (1-based)"
+	@echo "  VERBOSE_PLATFORMIO   If you want the full PIO output, set any value"
+	@echo "  GIT_RESET_HARD       Used by CI: reset all local changes. WARNING:"
+	@echo "                       THIS WILL UNDO ANY CHANGES YOU'VE MADE!"
+.PHONY: help
+
+tests-single-ci:
+	export GIT_RESET_HARD=true
+	$(MAKE) tests-single-local TEST_TARGET=$(TEST_TARGET)
+.PHONY: tests-single-ci
+
+tests-single-local:
+	@if ! test -n "$(TEST_TARGET)" ; then echo "***ERROR*** Set TEST_TARGET= or use make tests-all-local" ; return 1; fi
+	export PATH="./buildroot/bin/:./buildroot/tests/:${PATH}" \
+	  && export VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) \
+	  && run_tests . $(TEST_TARGET) "$(ONLY_TEST)"
+.PHONY: tests-single-local
+
+tests-single-local-docker:
+	@if ! test -n "$(TEST_TARGET)" ; then echo "***ERROR*** Set TEST_TARGET= or use make tests-all-local-docker" ; return 1; fi
+	docker-compose run --rm marlin $(MAKE) tests-single-local TEST_TARGET=$(TEST_TARGET) VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD) ONLY_TEST="$(ONLY_TEST)"
+.PHONY: tests-single-local-docker
+
+tests-all-local:
+	export PATH="./buildroot/bin/:./buildroot/tests/:${PATH}" \
+	  && export VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) \
+	  && for TEST_TARGET in $$(./get_test_targets.py) ; do echo "Running tests for $$TEST_TARGET" ; run_tests . $$TEST_TARGET ; done
+.PHONY: tests-all-local
+
+tests-all-local-docker:
+	docker-compose run --rm marlin $(MAKE) tests-all-local VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD)
+.PHONY: tests-all-local-docker
+
+setup-local-docker:
+	docker-compose build
+.PHONY: setup-local-docker
diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index 5f89483896..d0fd05b514 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -35,43 +35,33 @@
  *
  * Advanced settings can be found in Configuration_adv.h
  */
-#define CONFIGURATION_H_VERSION 020007
+#define CONFIGURATION_H_VERSION 02010200
 
 //===========================================================================
 //============================= Getting Started =============================
 //===========================================================================
 
 /**
- * Here are some standard links for getting your machine calibrated:
+ * Here are some useful links to help get your machine configured and calibrated:
  *
- * https://reprap.org/wiki/Calibration
- * https://youtu.be/wAL9d7FgInk
- * http://calculator.josefprusa.cz
- * https://reprap.org/wiki/Triffid_Hunter%27s_Calibration_Guide
- * https://www.thingiverse.com/thing:5573
- * https://sites.google.com/site/repraplogphase/calibration-of-your-reprap
- * https://www.thingiverse.com/thing:298812
+ * Example Configs:     https://github.com/MarlinFirmware/Configurations/branches/all
+ *
+ * Průša Calculator:    https://blog.prusaprinters.org/calculator_3416/
+ *
+ * Calibration Guides:  https://reprap.org/wiki/Calibration
+ *                      https://reprap.org/wiki/Triffid_Hunter%27s_Calibration_Guide
+ *                      https://sites.google.com/site/repraplogphase/calibration-of-your-reprap
+ *                      https://youtu.be/wAL9d7FgInk
+ *
+ * Calibration Objects: https://www.thingiverse.com/thing:5573
+ *                      https://www.thingiverse.com/thing:1278865
  */
 
-//===========================================================================
-//============================= DELTA Printer ===============================
-//===========================================================================
-// For a Delta printer start with one of the configuration files in the
-// config/examples/delta directory and customize for your machine.
-//
-
-//===========================================================================
-//============================= SCARA Printer ===============================
-//===========================================================================
-// For a SCARA printer start with the configuration files in
-// config/examples/SCARA and customize for your machine.
-//
-
 // @section info
 
 // Author info of this build printed to the host during boot and M115
 
-#define STRING_CONFIG_H_AUTHOR "(none, default config)" // Who made the changes.
+#define STRING_CONFIG_H_AUTHOR "Adam Bissen" // Who made the changes.
 
 //#define CUSTOM_VERSION_FILE Version.h // Path from the root directory (no quotes)
 
@@ -91,13 +81,18 @@
 
 // Show the bitmap in Marlin/_Bootscreen.h on startup.
 
-#define SHOW_CUSTOM_BOOTSCREEN
+//#define SHOW_CUSTOM_BOOTSCREEN
 
 // Show the bitmap in Marlin/_Statusscreen.h on the status screen.
 //#define CUSTOM_STATUS_SCREEN_IMAGE
 
 // @section machine
 
+// Choose the name from boards.h that matches your setup
+#ifndef MOTHERBOARD
+  #define MOTHERBOARD BOARD_BTT_SKR_V1_3
+#endif
+
 /**
  * Select the serial port on the board to use for communication with the host.
  * This allows the connection of wireless adapters (for instance) to non-default port pins.
@@ -106,18 +101,12 @@
  *
  * :[-1, 0, 1, 2, 3, 4, 5, 6, 7]
  */
-
 #define SERIAL_PORT -1
-#define SERIAL_PORT_2 0
 
 /**
- * Select a secondary serial port on the board to use for communication with the host.
- * :[-1, 0, 1, 2, 3, 4, 5, 6, 7]
- */
-//#define SERIAL_PORT_2 -1
-
-/**
- * This setting determines the communication speed of the printer.
+ * Serial Port Baud Rate
+ * This is the default communication speed for all serial ports.
+ * Set the baud rate defaults for additional serial ports below.
  *
  * 250000 works in most cases, but you might try a lower speed if
  * you commonly experience drop-outs during host printing.
@@ -127,14 +116,27 @@
  */
 #define BAUDRATE 250000
 
+//#define BAUD_RATE_GCODE     // Enable G-code M575 to set the baud rate
+
+/**
+ * Select a secondary serial port on the board to use for communication with the host.
+ * Currently Ethernet (-2) is only supported on Teensy 4.1 boards.
+ * :[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7]
+ */
+#define SERIAL_PORT_2 0
+#define BAUDRATE_2 250000   // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE
+
+/**
+ * Select a third serial port on the board to use for communication with the host.
+ * Currently only supported for AVR, DUE, LPC1768/9 and STM32/STM32F1
+ * :[-1, 0, 1, 2, 3, 4, 5, 6, 7]
+ */
+//#define SERIAL_PORT_3 1
+//#define BAUDRATE_3 250000   // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE
+
 // Enable the Bluetooth serial interface on AT90USB devices
 //#define BLUETOOTH
 
-// Choose the name from boards.h that matches your setup
-#ifndef MOTHERBOARD
-  #define MOTHERBOARD BOARD_BTT_SKR_V1_3
-#endif
-
 // Name displayed in the LCD "Ready" message and Info menu
 #define CUSTOM_MACHINE_NAME "SKR i3"
 
@@ -142,6 +144,88 @@
 // Choose your own or use a service like https://www.uuidgenerator.net/version4
 //#define MACHINE_UUID "00000000-0000-0000-0000-000000000000"
 
+// @section stepper drivers
+
+/**
+ * Stepper Drivers
+ *
+ * These settings allow Marlin to tune stepper driver timing and enable advanced options for
+ * stepper drivers that support them. You may also override timing options in Configuration_adv.h.
+ *
+ * Use TMC2208/TMC2208_STANDALONE for TMC2225 drivers and TMC2209/TMC2209_STANDALONE for TMC2226 drivers.
+ *
+ * Options: A4988, A5984, DRV8825, LV8729, TB6560, TB6600, TMC2100,
+ *          TMC2130, TMC2130_STANDALONE, TMC2160, TMC2160_STANDALONE,
+ *          TMC2208, TMC2208_STANDALONE, TMC2209, TMC2209_STANDALONE,
+ *          TMC26X,  TMC26X_STANDALONE,  TMC2660, TMC2660_STANDALONE,
+ *          TMC5130, TMC5130_STANDALONE, TMC5160, TMC5160_STANDALONE
+ * :['A4988', 'A5984', 'DRV8825', 'LV8729', 'TB6560', 'TB6600', 'TMC2100', 'TMC2130', 'TMC2130_STANDALONE', 'TMC2160', 'TMC2160_STANDALONE', 'TMC2208', 'TMC2208_STANDALONE', 'TMC2209', 'TMC2209_STANDALONE', 'TMC26X', 'TMC26X_STANDALONE', 'TMC2660', 'TMC2660_STANDALONE', 'TMC5130', 'TMC5130_STANDALONE', 'TMC5160', 'TMC5160_STANDALONE']
+ */
+#define X_DRIVER_TYPE  TMC2130
+#define Y_DRIVER_TYPE  TMC2130
+#define Z_DRIVER_TYPE  TMC2130
+//#define X2_DRIVER_TYPE A4988
+//#define Y2_DRIVER_TYPE A4988
+#define Z2_DRIVER_TYPE TMC2130
+//#define Z3_DRIVER_TYPE A4988
+//#define Z4_DRIVER_TYPE A4988
+//#define I_DRIVER_TYPE  A4988
+//#define J_DRIVER_TYPE  A4988
+//#define K_DRIVER_TYPE  A4988
+//#define U_DRIVER_TYPE  A4988
+//#define V_DRIVER_TYPE  A4988
+//#define W_DRIVER_TYPE  A4988
+#define E0_DRIVER_TYPE TMC2130
+//#define E1_DRIVER_TYPE A4988
+//#define E2_DRIVER_TYPE A4988
+//#define E3_DRIVER_TYPE A4988
+//#define E4_DRIVER_TYPE A4988
+//#define E5_DRIVER_TYPE A4988
+//#define E6_DRIVER_TYPE A4988
+//#define E7_DRIVER_TYPE A4988
+
+/**
+ * Additional Axis Settings
+ *
+ * Define AXISn_ROTATES for all axes that rotate or pivot.
+ * Rotational axis coordinates are expressed in degrees.
+ *
+ * AXISn_NAME defines the letter used to refer to the axis in (most) G-code commands.
+ * By convention the names and roles are typically:
+ *   'A' : Rotational axis parallel to X
+ *   'B' : Rotational axis parallel to Y
+ *   'C' : Rotational axis parallel to Z
+ *   'U' : Secondary linear axis parallel to X
+ *   'V' : Secondary linear axis parallel to Y
+ *   'W' : Secondary linear axis parallel to Z
+ *
+ * Regardless of these settings the axes are internally named I, J, K, U, V, W.
+ */
+#ifdef I_DRIVER_TYPE
+  #define AXIS4_NAME 'A' // :['A', 'B', 'C', 'U', 'V', 'W']
+  #define AXIS4_ROTATES
+#endif
+#ifdef J_DRIVER_TYPE
+  #define AXIS5_NAME 'B' // :['B', 'C', 'U', 'V', 'W']
+  #define AXIS5_ROTATES
+#endif
+#ifdef K_DRIVER_TYPE
+  #define AXIS6_NAME 'C' // :['C', 'U', 'V', 'W']
+  #define AXIS6_ROTATES
+#endif
+#ifdef U_DRIVER_TYPE
+  #define AXIS7_NAME 'U' // :['U', 'V', 'W']
+  //#define AXIS7_ROTATES
+#endif
+#ifdef V_DRIVER_TYPE
+  #define AXIS8_NAME 'V' // :['V', 'W']
+  //#define AXIS8_ROTATES
+#endif
+#ifdef W_DRIVER_TYPE
+  #define AXIS9_NAME 'W' // :['W']
+  //#define AXIS9_ROTATES
+#endif
+
 // @section extruder
 
 // This defines the number of extruders
@@ -161,34 +245,23 @@
   //#define SINGLENOZZLE_STANDBY_FAN
 #endif
 
-/**
- * Průša MK2 Single Nozzle Multi-Material Multiplexer, and variants.
- *
- * This device allows one stepper driver on a control board to drive
- * two to eight stepper motors, one at a time, in a manner suitable
- * for extruders.
- *
- * This option only allows the multiplexer to switch on tool-change.
- * Additional options to configure custom E moves are pending.
- */
-//#define MK2_MULTIPLEXER
-#if ENABLED(MK2_MULTIPLEXER)
-  // Override the default DIO selector pins here, if needed.
-  // Some pins files may provide defaults for these pins.
-  //#define E_MUX0_PIN 40  // Always Required
-  //#define E_MUX1_PIN 42  // Needed for 3 to 8 inputs
-  //#define E_MUX2_PIN 44  // Needed for 5 to 8 inputs
-#endif
+// @section multi-material
 
 /**
- * Průša Multi-Material Unit v2
+ * Multi-Material Unit
+ * Set to one of these predefined models:
+ *
+ *   PRUSA_MMU1           : Průša MMU1 (The "multiplexer" version)
+ *   PRUSA_MMU2           : Průša MMU2
+ *   PRUSA_MMU2S          : Průša MMU2S (Requires MK3S extruder with motion sensor, EXTRUDERS = 5)
+ *   EXTENDABLE_EMU_MMU2  : MMU with configurable number of filaments (ERCF, SMuFF or similar with Průša MMU2 compatible firmware)
+ *   EXTENDABLE_EMU_MMU2S : MMUS with configurable number of filaments (ERCF, SMuFF or similar with Průša MMU2 compatible firmware)
  *
  * Requires NOZZLE_PARK_FEATURE to park print head in case MMU unit fails.
- * Requires EXTRUDERS = 5
- *
- * For additional configuration see Configuration_adv.h
+ * See additional options in Configuration_adv.h.
+ * :["PRUSA_MMU1", "PRUSA_MMU2", "PRUSA_MMU2S", "EXTENDABLE_EMU_MMU2", "EXTENDABLE_EMU_MMU2S"]
  */
-//#define PRUSA_MMU2
+//#define MMU_MODEL PRUSA_MMU2
 
 // A dual extruder that uses a single stepper motor
 //#define SWITCHING_EXTRUDER
@@ -206,6 +279,7 @@
   #define SWITCHING_NOZZLE_SERVO_NR 0
   //#define SWITCHING_NOZZLE_E1_SERVO_NR 1          // If two servos are used, the index of the second
   #define SWITCHING_NOZZLE_SERVO_ANGLES { 0, 90 }   // Angles for E0, E1 (single servo) or lowered/raised (dual servo)
+  #define SWITCHING_NOZZLE_SERVO_DWELL 2500         // Dwell time to wait for servo to make physical move
 #endif
 
 /**
@@ -228,7 +302,6 @@
 
   #define PARKING_EXTRUDER_PARKING_X { -78, 184 }     // X positions for parking the extruders
   #define PARKING_EXTRUDER_GRAB_DISTANCE 1            // (mm) Distance to move beyond the parking point to grab the extruder
-  //#define MANUAL_SOLENOID_CONTROL                   // Manual control of docking solenoids with M380 S / M381
 
   #if ENABLED(PARKING_EXTRUDER)
 
@@ -310,6 +383,7 @@
   #define MIXING_VIRTUAL_TOOLS 16  // Use the Virtual Tool method with M163 and M164
   //#define DIRECT_MIXING_IN_G1    // Allow ABCDHI mix factors in G1 movement commands
   //#define GRADIENT_MIX           // Support for gradient mixing with M166 and LCD
+  //#define MIXING_PRESETS         // Assign 8 default V-tool presets for 2 or 3 MIXING_STEPPERS
   #if ENABLED(GRADIENT_MIX)
     //#define GRADIENT_VTOOL       // Add M166 T to use a V-tool index as a Gradient alias
   #endif
@@ -322,7 +396,7 @@
 //#define HOTEND_OFFSET_Y { 0.0, 5.00 }  // (mm) relative Y-offset for each nozzle
 //#define HOTEND_OFFSET_Z { 0.0, 0.00 }  // (mm) relative Z-offset for each nozzle
 
-// @section machine
+// @section psu control
 
 /**
  * Power Supply Control
@@ -334,10 +408,20 @@
 //#define PSU_NAME "Power Supply"
 
 #if ENABLED(PSU_CONTROL)
+  //#define MKS_PWC                 // Using the MKS PWC add-on
+  //#define PS_OFF_CONFIRM          // Confirm dialog when power off
+  //#define PS_OFF_SOUND            // Beep 1s when power off
   #define PSU_ACTIVE_STATE LOW      // Set 'LOW' for ATX, 'HIGH' for X-Box
 
-  //#define PSU_DEFAULT_OFF         // Keep power off until enabled directly with M80
-  //#define PSU_POWERUP_DELAY 250   // (ms) Delay for the PSU to warm up to full power
+  //#define PSU_DEFAULT_OFF               // Keep power off until enabled directly with M80
+  //#define PSU_POWERUP_DELAY      250    // (ms) Delay for the PSU to warm up to full power
+  //#define LED_POWEROFF_TIMEOUT 10000    // (ms) Turn off LEDs after power-off, with this amount of delay
+
+  //#define POWER_OFF_TIMER               // Enable M81 D to power off after a delay
+  //#define POWER_OFF_WAIT_FOR_COOLDOWN   // Enable M81 S to power off only after cooldown
+
+  //#define PSU_POWERUP_GCODE  "M355 S1"  // G-code to run after power-on (e.g., case light on)
+  //#define PSU_POWEROFF_GCODE "M355 S0"  // G-code to run before power-off (e.g., case light off)
 
   //#define AUTO_POWER_CONTROL      // Enable automatic control of the PS_ON pin
   #if ENABLED(AUTO_POWER_CONTROL)
@@ -345,9 +429,14 @@
     #define AUTO_POWER_E_FANS
     #define AUTO_POWER_CONTROLLERFAN
     #define AUTO_POWER_CHAMBER_FAN
-    //#define AUTO_POWER_E_TEMP        50 // (°C) Turn on PSU over this temperature
-    //#define AUTO_POWER_CHAMBER_TEMP  30 // (°C) Turn on PSU over this temperature
-    #define POWER_TIMEOUT 30
+    #define AUTO_POWER_COOLER_FAN
+    #define POWER_TIMEOUT              30 // (s) Turn off power if the machine is idle for this duration
+    //#define POWER_OFF_DELAY          60 // (s) Delay of poweroff after M81 command. Useful to let fans run for extra time.
+  #endif
+  #if EITHER(AUTO_POWER_CONTROL, POWER_OFF_WAIT_FOR_COOLDOWN)
+    //#define AUTO_POWER_E_TEMP        50 // (°C) PSU on if any extruder is over this temperature
+    //#define AUTO_POWER_CHAMBER_TEMP  30 // (°C) PSU on if the chamber is over this temperature
+    //#define AUTO_POWER_COOLER_TEMP   26 // (°C) PSU on if the cooler is over this temperature
   #endif
 #endif
 
@@ -357,68 +446,98 @@
 // @section temperature
 
 /**
- * --NORMAL IS 4.7kohm PULLUP!-- 1kohm pullup can be used on hotend sensor, using correct resistor and table
+ * --NORMAL IS 4.7kΩ PULLUP!-- 1kΩ pullup can be used on hotend sensor, using correct resistor and table
  *
  * Temperature sensors available:
  *
- *    -5 : PT100 / PT1000 with MAX31865 (only for sensors 0-1)
- *    -3 : thermocouple with MAX31855 (only for sensors 0-1)
- *    -2 : thermocouple with MAX6675 (only for sensors 0-1)
- *    -4 : thermocouple with AD8495
- *    -1 : thermocouple with AD595
+ *  SPI RTD/Thermocouple Boards - IMPORTANT: Read the NOTE below!
+ *  -------
+ *    -5 : MAX31865 with Pt100/Pt1000, 2, 3, or 4-wire  (only for sensors 0-1)
+ *                  NOTE: You must uncomment/set the MAX31865_*_OHMS_n defines below.
+ *    -3 : MAX31855 with Thermocouple, -200°C to +700°C (only for sensors 0-1)
+ *    -2 : MAX6675  with Thermocouple, 0°C to +700°C    (only for sensors 0-1)
+ *
+ *  NOTE: Ensure TEMP_n_CS_PIN is set in your pins file for each TEMP_SENSOR_n using an SPI Thermocouple. By default,
+ *        Hardware SPI on the default serial bus is used. If you have also set TEMP_n_SCK_PIN and TEMP_n_MISO_PIN,
+ *        Software SPI will be used on those ports instead. You can force Hardware SPI on the default bus in the
+ *        Configuration_adv.h file. At this time, separate Hardware SPI buses for sensors are not supported.
+ *
+ *  Analog Themocouple Boards
+ *  -------
+ *    -4 : AD8495 with Thermocouple
+ *    -1 : AD595  with Thermocouple
+ *
+ *  Analog Thermistors - 4.7kΩ pullup - Normal
+ *  -------
+ *     1 : 100kΩ  EPCOS - Best choice for EPCOS thermistors
+ *   331 : 100kΩ  Same as #1, but 3.3V scaled for MEGA
+ *   332 : 100kΩ  Same as #1, but 3.3V scaled for DUE
+ *     2 : 200kΩ  ATC Semitec 204GT-2
+ *   202 : 200kΩ  Copymaster 3D
+ *     3 : ???Ω   Mendel-parts thermistor
+ *     4 : 10kΩ   Generic Thermistor !! DO NOT use for a hotend - it gives bad resolution at high temp. !!
+ *     5 : 100kΩ  ATC Semitec 104GT-2/104NT-4-R025H42G - Used in ParCan, J-Head, and E3D, SliceEngineering 300°C
+ *   501 : 100kΩ  Zonestar - Tronxy X3A
+ *   502 : 100kΩ  Zonestar - used by hot bed in Zonestar Průša P802M
+ *   503 : 100kΩ  Zonestar (Z8XM2) Heated Bed thermistor
+ *   504 : 100kΩ  Zonestar P802QR2 (Part# QWG-104F-B3950) Hotend Thermistor
+ *   505 : 100kΩ  Zonestar P802QR2 (Part# QWG-104F-3950) Bed Thermistor
+ *   512 : 100kΩ  RPW-Ultra hotend
+ *     6 : 100kΩ  EPCOS - Not as accurate as table #1 (created using a fluke thermocouple)
+ *     7 : 100kΩ  Honeywell 135-104LAG-J01
+ *    71 : 100kΩ  Honeywell 135-104LAF-J01
+ *     8 : 100kΩ  Vishay 0603 SMD NTCS0603E3104FXT
+ *     9 : 100kΩ  GE Sensing AL03006-58.2K-97-G1
+ *    10 : 100kΩ  RS PRO 198-961
+ *    11 : 100kΩ  Keenovo AC silicone mats, most Wanhao i3 machines - beta 3950, 1%
+ *    12 : 100kΩ  Vishay 0603 SMD NTCS0603E3104FXT (#8) - calibrated for Makibox hot bed
+ *    13 : 100kΩ  Hisens up to 300°C - for "Simple ONE" & "All In ONE" hotend - beta 3950, 1%
+ *    15 : 100kΩ  Calibrated for JGAurora A5 hotend
+ *    18 : 200kΩ  ATC Semitec 204GT-2 Dagoma.Fr - MKS_Base_DKU001327
+ *    22 : 100kΩ  GTM32 Pro vB - hotend - 4.7kΩ pullup to 3.3V and 220Ω to analog input
+ *    23 : 100kΩ  GTM32 Pro vB - bed - 4.7kΩ pullup to 3.3v and 220Ω to analog input
+ *    30 : 100kΩ  Kis3d Silicone heating mat 200W/300W with 6mm precision cast plate (EN AW 5083) NTC100K - beta 3950
+ *    60 : 100kΩ  Maker's Tool Works Kapton Bed Thermistor - beta 3950
+ *    61 : 100kΩ  Formbot/Vivedino 350°C Thermistor - beta 3950
+ *    66 : 4.7MΩ  Dyze Design / Trianglelab T-D500 500°C High Temperature Thermistor
+ *    67 : 500kΩ  SliceEngineering 450°C Thermistor
+ *    68 : PT100 amplifier board from Dyze Design
+ *    70 : 100kΩ  bq Hephestos 2
+ *    75 : 100kΩ  Generic Silicon Heat Pad with NTC100K MGB18-104F39050L32
+ *  2000 : 100kΩ  Ultimachine Rambo TDK NTCG104LH104KT1 NTC100K motherboard Thermistor
+ *
+ *  Analog Thermistors - 1kΩ pullup - Atypical, and requires changing out the 4.7kΩ pullup for 1kΩ.
+ *  -------                           (but gives greater accuracy and more stable PID)
+ *    51 : 100kΩ  EPCOS (1kΩ pullup)
+ *    52 : 200kΩ  ATC Semitec 204GT-2 (1kΩ pullup)
+ *    55 : 100kΩ  ATC Semitec 104GT-2 - Used in ParCan & J-Head (1kΩ pullup)
+ *
+ *  Analog Thermistors - 10kΩ pullup - Atypical
+ *  -------
+ *    99 : 100kΩ  Found on some Wanhao i3 machines with a 10kΩ pull-up resistor
+ *
+ *  Analog RTDs (Pt100/Pt1000)
+ *  -------
+ *   110 : Pt100  with 1kΩ pullup (atypical)
+ *   147 : Pt100  with 4.7kΩ pullup
+ *  1010 : Pt1000 with 1kΩ pullup (atypical)
+ *  1022 : Pt1000 with 2.2kΩ pullup
+ *  1047 : Pt1000 with 4.7kΩ pullup (E3D)
+ *    20 : Pt100  with circuit in the Ultimainboard V2.x with mainboard ADC reference voltage = INA826 amplifier-board supply voltage.
+ *                NOTE: (1) Must use an ADC input with no pullup. (2) Some INA826 amplifiers are unreliable at 3.3V so consider using sensor 147, 110, or 21.
+ *    21 : Pt100  with circuit in the Ultimainboard V2.x with 3.3v ADC reference voltage (STM32, LPC176x....) and 5V INA826 amplifier board supply.
+ *                NOTE: ADC pins are not 5V tolerant. Not recommended because it's possible to damage the CPU by going over 500°C.
+ *   201 : Pt100  with circuit in Overlord, similar to Ultimainboard V2.x
+ *
+ *  Custom/Dummy/Other Thermal Sensors
+ *  ------
  *     0 : not used
- *     1 : 100k thermistor - best choice for EPCOS 100k (4.7k pullup)
- *   331 : (3.3V scaled thermistor 1 table for MEGA)
- *   332 : (3.3V scaled thermistor 1 table for DUE)
- *     2 : 200k thermistor - ATC Semitec 204GT-2 (4.7k pullup)
- *   202 : 200k thermistor - Copymaster 3D
- *     3 : Mendel-parts thermistor (4.7k pullup)
- *     4 : 10k thermistor !! do not use it for a hotend. It gives bad resolution at high temp. !!
- *     5 : 100K thermistor - ATC Semitec 104GT-2/104NT-4-R025H42G (Used in ParCan, J-Head, and E3D) (4.7k pullup)
- *   501 : 100K Zonestar (Tronxy X3A) Thermistor
- *   502 : 100K Zonestar Thermistor used by hot bed in Zonestar Průša P802M
- *   512 : 100k RPW-Ultra hotend thermistor (4.7k pullup)
- *     6 : 100k EPCOS - Not as accurate as table 1 (created using a fluke thermocouple) (4.7k pullup)
- *     7 : 100k Honeywell thermistor 135-104LAG-J01 (4.7k pullup)
- *    71 : 100k Honeywell thermistor 135-104LAF-J01 (4.7k pullup)
- *     8 : 100k 0603 SMD Vishay NTCS0603E3104FXT (4.7k pullup)
- *     9 : 100k GE Sensing AL03006-58.2K-97-G1 (4.7k pullup)
- *    10 : 100k RS thermistor 198-961 (4.7k pullup)
- *    11 : 100k beta 3950 1% thermistor (Used in Keenovo AC silicone mats and most Wanhao i3 machines) (4.7k pullup)
- *    12 : 100k 0603 SMD Vishay NTCS0603E3104FXT (4.7k pullup) (calibrated for Makibox hot bed)
- *    13 : 100k Hisens 3950  1% up to 300°C for hotend "Simple ONE " & "Hotend "All In ONE"
- *    15 : 100k thermistor calibration for JGAurora A5 hotend
- *    18 : ATC Semitec 204GT-2 (4.7k pullup) Dagoma.Fr - MKS_Base_DKU001327
- *    20 : Pt100 with circuit in the Ultimainboard V2.x with 5v excitation (AVR)
- *    21 : Pt100 with circuit in the Ultimainboard V2.x with 3.3v excitation (STM32 \ LPC176x....)
- *    22 : 100k (hotend) with 4.7k pullup to 3.3V and 220R to analog input (as in GTM32 Pro vB)
- *    23 : 100k (bed) with 4.7k pullup to 3.3v and 220R to analog input (as in GTM32 Pro vB)
- *    30 : Kis3d Silicone heating mat 200W/300W with 6mm precision cast plate (EN AW 5083) NTC100K / B3950 (4.7k pullup)
- *   201 : Pt100 with circuit in Overlord, similar to Ultimainboard V2.x
- *    60 : 100k Maker's Tool Works Kapton Bed Thermistor beta=3950
- *    61 : 100k Formbot / Vivedino 3950 350C thermistor 4.7k pullup
- *    66 : 4.7M High Temperature thermistor from Dyze Design
- *    67 : 450C thermistor from SliceEngineering
- *    70 : the 100K thermistor found in the bq Hephestos 2
- *    75 : 100k Generic Silicon Heat Pad with NTC 100K MGB18-104F39050L32 thermistor
- *    99 : 100k thermistor with a 10K pull-up resistor (found on some Wanhao i3 machines)
- *
- *       1k ohm pullup tables - This is atypical, and requires changing out the 4.7k pullup for 1k.
- *                              (but gives greater accuracy and more stable PID)
- *    51 : 100k thermistor - EPCOS (1k pullup)
- *    52 : 200k thermistor - ATC Semitec 204GT-2 (1k pullup)
- *    55 : 100k thermistor - ATC Semitec 104GT-2 (Used in ParCan & J-Head) (1k pullup)
- *
- *  1047 : Pt1000 with 4k7 pullup (E3D)
- *  1010 : Pt1000 with 1k pullup (non standard)
- *   147 : Pt100 with 4k7 pullup
- *   110 : Pt100 with 1k pullup (non standard)
- *
  *  1000 : Custom - Specify parameters in Configuration_adv.h
  *
- *         Use these for Testing or Development purposes. NEVER for production machine.
+ *   !!! Use these for Testing or Development purposes. NEVER for production machine. !!!
  *   998 : Dummy Table that ALWAYS reads 25°C or the temperature defined below.
  *   999 : Dummy Table that ALWAYS reads 100°C or the temperature defined below.
+ *
  */
 #define TEMP_SENSOR_0 1
 #define TEMP_SENSOR_1 0
@@ -431,29 +550,61 @@
 #define TEMP_SENSOR_BED 1
 #define TEMP_SENSOR_PROBE 0
 #define TEMP_SENSOR_CHAMBER 0
+#define TEMP_SENSOR_COOLER 0
+#define TEMP_SENSOR_BOARD 0
+#define TEMP_SENSOR_REDUNDANT 0
 
 // Dummy thermistor constant temperature readings, for use with 998 and 999
-#define DUMMY_THERMISTOR_998_VALUE 25
+#define DUMMY_THERMISTOR_998_VALUE  25
 #define DUMMY_THERMISTOR_999_VALUE 100
 
-// Resistor values when using a MAX31865 (sensor -5)
-// Sensor value is typically 100 (PT100) or 1000 (PT1000)
-// Calibration value is typically 430 ohm for AdaFruit PT100 modules and 4300 ohm for AdaFruit PT1000 modules.
-//#define MAX31865_SENSOR_OHMS      100
-//#define MAX31865_CALIBRATION_OHMS 430
+// Resistor values when using MAX31865 sensors (-5) on TEMP_SENSOR_0 / 1
+#if TEMP_SENSOR_IS_MAX_TC(0)
+  #define MAX31865_SENSOR_OHMS_0      100 // (Ω) Typically 100 or 1000 (PT100 or PT1000)
+  #define MAX31865_CALIBRATION_OHMS_0 430 // (Ω) Typically 430 for Adafruit PT100; 4300 for Adafruit PT1000
+#endif
+#if TEMP_SENSOR_IS_MAX_TC(1)
+  #define MAX31865_SENSOR_OHMS_1      100
+  #define MAX31865_CALIBRATION_OHMS_1 430
+#endif
+#if TEMP_SENSOR_IS_MAX_TC(2)
+  #define MAX31865_SENSOR_OHMS_2      100
+  #define MAX31865_CALIBRATION_OHMS_2 430
+#endif
 
-// Use temp sensor 1 as a redundant sensor with sensor 0. If the readings
-// from the two sensors differ too much the print will be aborted.
-//#define TEMP_SENSOR_1_AS_REDUNDANT
-#define MAX_REDUNDANT_TEMP_SENSOR_DIFF 10
+#if HAS_E_TEMP_SENSOR
+  #define TEMP_RESIDENCY_TIME         10  // (seconds) Time to wait for hotend to "settle" in M109
+  #define TEMP_WINDOW                  1  // (°C) Temperature proximity for the "temperature reached" timer
+  #define TEMP_HYSTERESIS              3  // (°C) Temperature proximity considered "close enough" to the target
+#endif
 
-#define TEMP_RESIDENCY_TIME     10  // (seconds) Time to wait for hotend to "settle" in M109
-#define TEMP_WINDOW              1  // (°C) Temperature proximity for the "temperature reached" timer
-#define TEMP_HYSTERESIS          3  // (°C) Temperature proximity considered "close enough" to the target
+#if TEMP_SENSOR_BED
+  #define TEMP_BED_RESIDENCY_TIME     10  // (seconds) Time to wait for bed to "settle" in M190
+  #define TEMP_BED_WINDOW              1  // (°C) Temperature proximity for the "temperature reached" timer
+  #define TEMP_BED_HYSTERESIS          3  // (°C) Temperature proximity considered "close enough" to the target
+#endif
 
-#define TEMP_BED_RESIDENCY_TIME 10  // (seconds) Time to wait for bed to "settle" in M190
-#define TEMP_BED_WINDOW          1  // (°C) Temperature proximity for the "temperature reached" timer
-#define TEMP_BED_HYSTERESIS      3  // (°C) Temperature proximity considered "close enough" to the target
+#if TEMP_SENSOR_CHAMBER
+  #define TEMP_CHAMBER_RESIDENCY_TIME 10  // (seconds) Time to wait for chamber to "settle" in M191
+  #define TEMP_CHAMBER_WINDOW          1  // (°C) Temperature proximity for the "temperature reached" timer
+  #define TEMP_CHAMBER_HYSTERESIS      3  // (°C) Temperature proximity considered "close enough" to the target
+#endif
+
+/**
+ * Redundant Temperature Sensor (TEMP_SENSOR_REDUNDANT)
+ *
+ * Use a temp sensor as a redundant sensor for another reading. Select an unused temperature sensor, and another
+ * sensor you'd like it to be redundant for. If the two thermistors differ by TEMP_SENSOR_REDUNDANT_MAX_DIFF (°C),
+ * the print will be aborted. Whichever sensor is selected will have its normal functions disabled; i.e. selecting
+ * the Bed sensor (-1) will disable bed heating/monitoring.
+ *
+ * For selecting source/target use: COOLER, PROBE, BOARD, CHAMBER, BED, E0, E1, E2, E3, E4, E5, E6, E7
+ */
+#if TEMP_SENSOR_REDUNDANT
+  #define TEMP_SENSOR_REDUNDANT_SOURCE    E1  // The sensor that will provide the redundant reading.
+  #define TEMP_SENSOR_REDUNDANT_TARGET    E0  // The sensor that we are providing a redundant reading for.
+  #define TEMP_SENSOR_REDUNDANT_MAX_DIFF  10  // (°C) Temperature difference that will trigger a print abort.
+#endif
 
 // Below this temperature the heater will be switched off
 // because it probably indicates a broken thermistor wire.
@@ -466,40 +617,44 @@
 #define HEATER_6_MINTEMP   5
 #define HEATER_7_MINTEMP   5
 #define BED_MINTEMP        5
+#define CHAMBER_MINTEMP    5
 
 // Above this temperature the heater will be switched off.
 // This can protect components from overheating, but NOT from shorts and failures.
 // (Use MINTEMP for thermistor short/failure protection.)
-#define HEATER_0_MAXTEMP 260
-#define HEATER_1_MAXTEMP 260
-#define HEATER_2_MAXTEMP 260
-#define HEATER_3_MAXTEMP 260
-#define HEATER_4_MAXTEMP 260
-#define HEATER_5_MAXTEMP 260
-#define HEATER_6_MAXTEMP 260
-#define HEATER_7_MAXTEMP 260
+#define HEATER_0_MAXTEMP 275
+#define HEATER_1_MAXTEMP 275
+#define HEATER_2_MAXTEMP 275
+#define HEATER_3_MAXTEMP 275
+#define HEATER_4_MAXTEMP 275
+#define HEATER_5_MAXTEMP 275
+#define HEATER_6_MAXTEMP 275
+#define HEATER_7_MAXTEMP 275
 #define BED_MAXTEMP      115
 
 //===========================================================================
 //============================= PID Settings ================================
 //===========================================================================
-// PID Tuning Guide here: https://reprap.org/wiki/PID_Tuning
 
-// Comment the following line to disable PID and enable bang-bang.
-#define PIDTEMP
+// @section hotend temp
+
+// Enable PIDTEMP for PID control or MPCTEMP for Predictive Model.
+// temperature control. Disable both for bang-bang heating.
+#define PIDTEMP          // See the PID Tuning Guide at https://reprap.org/wiki/PID_Tuning
+//#define MPCTEMP        // ** EXPERIMENTAL **
+
 #define BANG_MAX 255     // Limits current to nozzle while in bang-bang mode; 255=full current
 #define PID_MAX BANG_MAX // Limits current to nozzle while PID is active (see PID_FUNCTIONAL_RANGE below); 255=full current
 #define PID_K1 0.95      // Smoothing factor within any PID loop
 
 #if ENABLED(PIDTEMP)
-  //#define PID_EDIT_MENU         // Add PID editing to the "Advanced Settings" menu. (~700 bytes of PROGMEM)
-  //#define PID_AUTOTUNE_MENU     // Add PID auto-tuning to the "Advanced Settings" menu. (~250 bytes of PROGMEM)
-  //#define PID_PARAMS_PER_HOTEND // Uses separate PID parameters for each extruder (useful for mismatched extruders)
-                                  // Set/get with gcode: M301 E[extruder number, 0-2]
+  //#define PID_DEBUG             // Print PID debug data to the serial port. Use 'M303 D' to toggle activation.
+  //#define PID_PARAMS_PER_HOTEND // Use separate PID parameters for each extruder (useful for mismatched extruders)
+                                  // Set/get with G-code: M301 E[extruder number, 0-2]
 
   #if ENABLED(PID_PARAMS_PER_HOTEND)
-    // Specify between 1 and HOTENDS values per array.
-    // If fewer than EXTRUDER values are provided, the last element will be repeated.
+    // Specify up to one value per hotend here, according to your setup.
+    // If there are fewer values, the last one applies to the remaining hotends.
     #define DEFAULT_Kp_LIST {  22.20,  22.20 }
     #define DEFAULT_Ki_LIST {   1.08,   1.08 }
     #define DEFAULT_Kd_LIST { 114.00, 114.00 }
@@ -509,7 +664,50 @@
     #define DEFAULT_Ki   1.42
     #define DEFAULT_Kd  98.72
   #endif
-#endif // PIDTEMP
+#endif
+
+/**
+ * Model Predictive Control for hotend
+ *
+ * Use a physical model of the hotend to control temperature. When configured correctly
+ * this gives better responsiveness and stability than PID and it also removes the need
+ * for PID_EXTRUSION_SCALING and PID_FAN_SCALING. Use M306 T to autotune the model.
+ * @section mpctemp
+ */
+#if ENABLED(MPCTEMP)
+  //#define MPC_EDIT_MENU                             // Add MPC editing to the "Advanced Settings" menu. (~1300 bytes of flash)
+  //#define MPC_AUTOTUNE_MENU                         // Add MPC auto-tuning to the "Advanced Settings" menu. (~350 bytes of flash)
+
+  #define MPC_MAX BANG_MAX                            // (0..255) Current to nozzle while MPC is active.
+  #define MPC_HEATER_POWER { 40.0f }                  // (W) Heat cartridge powers.
+
+  #define MPC_INCLUDE_FAN                             // Model the fan speed?
+
+  // Measured physical constants from M306
+  #define MPC_BLOCK_HEAT_CAPACITY { 16.7f }           // (J/K) Heat block heat capacities.
+  #define MPC_SENSOR_RESPONSIVENESS { 0.22f }         // (K/s per ∆K) Rate of change of sensor temperature from heat block.
+  #define MPC_AMBIENT_XFER_COEFF { 0.068f }           // (W/K) Heat transfer coefficients from heat block to room air with fan off.
+  #if ENABLED(MPC_INCLUDE_FAN)
+    #define MPC_AMBIENT_XFER_COEFF_FAN255 { 0.097f }  // (W/K) Heat transfer coefficients from heat block to room air with fan on full.
+  #endif
+
+  // For one fan and multiple hotends MPC needs to know how to apply the fan cooling effect.
+  #if ENABLED(MPC_INCLUDE_FAN)
+    //#define MPC_FAN_0_ALL_HOTENDS
+    //#define MPC_FAN_0_ACTIVE_HOTEND
+  #endif
+
+  #define FILAMENT_HEAT_CAPACITY_PERMM { 5.6e-3f }    // 0.0056 J/K/mm for 1.75mm PLA (0.0149 J/K/mm for 2.85mm PLA).
+  //#define FILAMENT_HEAT_CAPACITY_PERMM { 3.6e-3f }  // 0.0036 J/K/mm for 1.75mm PETG (0.0094 J/K/mm for 2.85mm PETG).
+
+  // Advanced options
+  #define MPC_SMOOTHING_FACTOR 0.5f                   // (0.0...1.0) Noisy temperature sensors may need a lower value for stabilization.
+  #define MPC_MIN_AMBIENT_CHANGE 1.0f                 // (K/s) Modeled ambient temperature rate of change, when correcting model inaccuracies.
+  #define MPC_STEADYSTATE 0.5f                        // (K/s) Temperature change rate for steady state logic to be enforced.
+
+  #define MPC_TUNING_POS { X_CENTER, Y_CENTER, 1.0f } // (mm) M306 Autotuning position, ideally bed center at first layer height.
+  #define MPC_TUNING_END_Z 10.0f                      // (mm) M306 Autotuning final Z position.
+#endif
 
 //===========================================================================
 //====================== PID > Bed Temperature Control ======================
@@ -527,6 +725,7 @@
  * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 250W
  * heater. If your configuration is significantly different than this and you don't understand
  * the issues involved, don't use bed PID until someone else verifies that your hardware works.
+ * @section bed temp
  */
 #define PIDTEMPBED
 
@@ -542,7 +741,7 @@
 
 #if ENABLED(PIDTEMPBED)
   //#define MIN_BED_POWER 0
-  //#define PID_BED_DEBUG // Sends debug data to the serial port.
+  //#define PID_BED_DEBUG // Print Bed PID debug data to the serial port.
 
   // 120V 250W silicone heater into 4mm borosilicate (MendelMax 1.5+)
   // from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, aggressive factor of .15 (vs .1, 1, 10)
@@ -553,15 +752,62 @@
   // FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles.
 #endif // PIDTEMPBED
 
-#if EITHER(PIDTEMP, PIDTEMPBED)
-  //#define PID_DEBUG             // Sends debug data to the serial port. Use 'M303 D' to toggle activation.
+//===========================================================================
+//==================== PID > Chamber Temperature Control ====================
+//===========================================================================
+
+/**
+ * PID Chamber Heating
+ *
+ * If this option is enabled set PID constants below.
+ * If this option is disabled, bang-bang will be used and CHAMBER_LIMIT_SWITCHING will enable
+ * hysteresis.
+ *
+ * The PID frequency will be the same as the extruder PWM.
+ * If PID_dT is the default, and correct for the hardware/configuration, that means 7.689Hz,
+ * which is fine for driving a square wave into a resistive load and does not significantly
+ * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 200W
+ * heater. If your configuration is significantly different than this and you don't understand
+ * the issues involved, don't use chamber PID until someone else verifies that your hardware works.
+ * @section chamber temp
+ */
+//#define PIDTEMPCHAMBER
+//#define CHAMBER_LIMIT_SWITCHING
+
+/**
+ * Max Chamber Power
+ * Applies to all forms of chamber control (PID, bang-bang, and bang-bang with hysteresis).
+ * When set to any value below 255, enables a form of PWM to the chamber heater that acts like a divider
+ * so don't use it unless you are OK with PWM on your heater. (See the comment on enabling PIDTEMPCHAMBER)
+ */
+#define MAX_CHAMBER_POWER 255 // limits duty cycle to chamber heater; 255=full current
+
+#if ENABLED(PIDTEMPCHAMBER)
+  #define MIN_CHAMBER_POWER 0
+  //#define PID_CHAMBER_DEBUG // Print Chamber PID debug data to the serial port.
+
+  // Lasko "MyHeat Personal Heater" (200w) modified with a Fotek SSR-10DA to control only the heating element
+  // and placed inside the small Creality printer enclosure tent.
+  //
+  #define DEFAULT_chamberKp 37.04
+  #define DEFAULT_chamberKi 1.40
+  #define DEFAULT_chamberKd 655.17
+  // M309 P37.04 I1.04 D655.17
+
+  // FIND YOUR OWN: "M303 E-2 C8 S50" to run autotune on the chamber at 50 degreesC for 8 cycles.
+#endif // PIDTEMPCHAMBER
+
+#if ANY(PIDTEMP, PIDTEMPBED, PIDTEMPCHAMBER)
   //#define PID_OPENLOOP          // Puts PID in open loop. M104/M140 sets the output power from 0 to PID_MAX
   //#define SLOW_PWM_HEATERS      // PWM with very low frequency (roughly 0.125Hz=8s) and minimum state time of approximately 1s useful for heaters driven by a relay
   #define PID_FUNCTIONAL_RANGE 10 // If the temperature difference between the target temperature and the actual temperature
                                   // is more than PID_FUNCTIONAL_RANGE then the PID will be shut off and the heater will be set to min/max.
+
+  //#define PID_EDIT_MENU         // Add PID editing to the "Advanced Settings" menu. (~700 bytes of flash)
+  //#define PID_AUTOTUNE_MENU     // Add PID auto-tuning to the "Advanced Settings" menu. (~250 bytes of flash)
 #endif
 
-// @section extruder
+// @section safety
 
 /**
  * Prevent extrusion if the temperature is below EXTRUDE_MINTEMP.
@@ -600,6 +846,7 @@
 #define THERMAL_PROTECTION_HOTENDS // Enable thermal protection for all extruders
 #define THERMAL_PROTECTION_BED     // Enable thermal protection for the heated bed
 #define THERMAL_PROTECTION_CHAMBER // Enable thermal protection for the heated chamber
+#define THERMAL_PROTECTION_COOLER  // Enable thermal protection for the laser cooling
 
 //===========================================================================
 //============================= Mechanical Settings =========================
@@ -616,12 +863,164 @@
 //#define COREZX
 //#define COREZY
 //#define MARKFORGED_XY  // MarkForged. See https://reprap.org/forum/read.php?152,504042
+//#define MARKFORGED_YX
+
+// Enable for a belt style printer with endless "Z" motion
+//#define BELTPRINTER
+
+// Enable for Polargraph Kinematics
+//#define POLARGRAPH
+#if ENABLED(POLARGRAPH)
+  #define POLARGRAPH_MAX_BELT_LEN 1035.0
+  #define DEFAULT_SEGMENTS_PER_SECOND 5
+#endif
+
+// @section delta
+
+// Enable for DELTA kinematics and configure below
+//#define DELTA
+#if ENABLED(DELTA)
+
+  // Make delta curves from many straight lines (linear interpolation).
+  // This is a trade-off between visible corners (not enough segments)
+  // and processor overload (too many expensive sqrt calls).
+  #define DEFAULT_SEGMENTS_PER_SECOND 200
+
+  // After homing move down to a height where XY movement is unconstrained
+  //#define DELTA_HOME_TO_SAFE_ZONE
+
+  // Delta calibration menu
+  // Add three-point calibration to the MarlinUI menu.
+  // See http://minow.blogspot.com/index.html#4918805519571907051
+  //#define DELTA_CALIBRATION_MENU
+
+  // G33 Delta Auto-Calibration. Enable EEPROM_SETTINGS to store results.
+  //#define DELTA_AUTO_CALIBRATION
+
+  #if ENABLED(DELTA_AUTO_CALIBRATION)
+    // Default number of probe points : n*n (1 -> 7)
+    #define DELTA_CALIBRATION_DEFAULT_POINTS 4
+  #endif
+
+  #if EITHER(DELTA_AUTO_CALIBRATION, DELTA_CALIBRATION_MENU)
+    // Step size for paper-test probing
+    #define PROBE_MANUALLY_STEP 0.05      // (mm)
+  #endif
+
+  // Print surface diameter/2 minus unreachable space (avoid collisions with vertical towers).
+  #define DELTA_PRINTABLE_RADIUS 140.0    // (mm)
+
+  // Maximum reachable area
+  #define DELTA_MAX_RADIUS       140.0    // (mm)
+
+  // Center-to-center distance of the holes in the diagonal push rods.
+  #define DELTA_DIAGONAL_ROD 250.0        // (mm)
+
+  // Distance between bed and nozzle Z home position
+  #define DELTA_HEIGHT 250.00             // (mm) Get this value from G33 auto calibrate
+
+  #define DELTA_ENDSTOP_ADJ { 0.0, 0.0, 0.0 } // Get these values from G33 auto calibrate
+
+  // Horizontal distance bridged by diagonal push rods when effector is centered.
+  #define DELTA_RADIUS 124.0              // (mm) Get this value from G33 auto calibrate
+
+  // Trim adjustments for individual towers
+  // tower angle corrections for X and Y tower / rotate XYZ so Z tower angle = 0
+  // measured in degrees anticlockwise looking from above the printer
+  #define DELTA_TOWER_ANGLE_TRIM { 0.0, 0.0, 0.0 } // Get these values from G33 auto calibrate
+
+  // Delta radius and diagonal rod adjustments (mm)
+  //#define DELTA_RADIUS_TRIM_TOWER { 0.0, 0.0, 0.0 }
+  //#define DELTA_DIAGONAL_ROD_TRIM_TOWER { 0.0, 0.0, 0.0 }
+#endif
+
+// @section scara
+
+/**
+ * MORGAN_SCARA was developed by QHARLEY in South Africa in 2012-2013.
+ * Implemented and slightly reworked by JCERNY in June, 2014.
+ *
+ * Mostly Printed SCARA is an open source design by Tyler Williams. See:
+ *   https://www.thingiverse.com/thing:2487048
+ *   https://www.thingiverse.com/thing:1241491
+ */
+//#define MORGAN_SCARA
+//#define MP_SCARA
+#if EITHER(MORGAN_SCARA, MP_SCARA)
+  // If movement is choppy try lowering this value
+  #define DEFAULT_SEGMENTS_PER_SECOND 200
+
+  // Length of inner and outer support arms. Measure arm lengths precisely.
+  #define SCARA_LINKAGE_1 150       // (mm)
+  #define SCARA_LINKAGE_2 150       // (mm)
+
+  // SCARA tower offset (position of Tower relative to bed zero position)
+  // This needs to be reasonably accurate as it defines the printbed position in the SCARA space.
+  #define SCARA_OFFSET_X  100       // (mm)
+  #define SCARA_OFFSET_Y  -56       // (mm)
+
+  #if ENABLED(MORGAN_SCARA)
+
+    //#define DEBUG_SCARA_KINEMATICS
+    #define SCARA_FEEDRATE_SCALING  // Convert XY feedrate from mm/s to degrees/s on the fly
+
+    // Radius around the center where the arm cannot reach
+    #define MIDDLE_DEAD_ZONE_R   0  // (mm)
+
+    #define THETA_HOMING_OFFSET  0  // Calculated from Calibration Guide and M360 / M114. See http://reprap.harleystudio.co.za/?page_id=1073
+    #define PSI_HOMING_OFFSET    0  // Calculated from Calibration Guide and M364 / M114. See http://reprap.harleystudio.co.za/?page_id=1073
+
+  #elif ENABLED(MP_SCARA)
+
+    #define SCARA_OFFSET_THETA1  12 // degrees
+    #define SCARA_OFFSET_THETA2 131 // degrees
+
+  #endif
+
+#endif
+
+// @section tpara
+
+// Enable for TPARA kinematics and configure below
+//#define AXEL_TPARA
+#if ENABLED(AXEL_TPARA)
+  #define DEBUG_TPARA_KINEMATICS
+  #define DEFAULT_SEGMENTS_PER_SECOND 200
+
+  // Length of inner and outer support arms. Measure arm lengths precisely.
+  #define TPARA_LINKAGE_1 120       // (mm)
+  #define TPARA_LINKAGE_2 120       // (mm)
+
+  // SCARA tower offset (position of Tower relative to bed zero position)
+  // This needs to be reasonably accurate as it defines the printbed position in the SCARA space.
+  #define TPARA_OFFSET_X    0       // (mm)
+  #define TPARA_OFFSET_Y    0       // (mm)
+  #define TPARA_OFFSET_Z    0       // (mm)
+
+  #define SCARA_FEEDRATE_SCALING  // Convert XY feedrate from mm/s to degrees/s on the fly
+
+  // Radius around the center where the arm cannot reach
+  #define MIDDLE_DEAD_ZONE_R   0  // (mm)
+
+  // Calculated from Calibration Guide and M360 / M114. See http://reprap.harleystudio.co.za/?page_id=1073
+  #define THETA_HOMING_OFFSET  0
+  #define PSI_HOMING_OFFSET    0
+#endif
+
+// @section machine
+
+// Articulated robot (arm). Joints are directly mapped to axes with no kinematics.
+//#define ARTICULATED_ROBOT_ARM
+
+// For a hot wire cutter with parallel horizontal axes (X, I) where the heights of the two wire
+// ends are controlled by parallel axes (Y, J). Joints are directly mapped to axes (no kinematics).
+//#define FOAMCUTTER_XYUV
 
 //===========================================================================
 //============================== Endstop Settings ===========================
 //===========================================================================
 
-// @section homing
+// @section endstops
 
 // Specify here all the endstop connectors that are connected to any endstop or probe.
 // Almost all printers will be using one per axis. Probes will use one or more of the
@@ -629,20 +1028,44 @@
 #define USE_XMIN_PLUG
 #define USE_YMIN_PLUG
 #define USE_ZMIN_PLUG
+//#define USE_IMIN_PLUG
+//#define USE_JMIN_PLUG
+//#define USE_KMIN_PLUG
+//#define USE_UMIN_PLUG
+//#define USE_VMIN_PLUG
+//#define USE_WMIN_PLUG
 //#define USE_XMAX_PLUG
 //#define USE_YMAX_PLUG
 //#define USE_ZMAX_PLUG
+//#define USE_IMAX_PLUG
+//#define USE_JMAX_PLUG
+//#define USE_KMAX_PLUG
+//#define USE_UMAX_PLUG
+//#define USE_VMAX_PLUG
+//#define USE_WMAX_PLUG
 
 // Enable pullup for all endstops to prevent a floating state
 #define ENDSTOPPULLUPS
 #if DISABLED(ENDSTOPPULLUPS)
   // Disable ENDSTOPPULLUPS to set pullups individually
-  //#define ENDSTOPPULLUP_XMAX
-  //#define ENDSTOPPULLUP_YMAX
-  //#define ENDSTOPPULLUP_ZMAX
   //#define ENDSTOPPULLUP_XMIN
   //#define ENDSTOPPULLUP_YMIN
   //#define ENDSTOPPULLUP_ZMIN
+  //#define ENDSTOPPULLUP_IMIN
+  //#define ENDSTOPPULLUP_JMIN
+  //#define ENDSTOPPULLUP_KMIN
+  //#define ENDSTOPPULLUP_UMIN
+  //#define ENDSTOPPULLUP_VMIN
+  //#define ENDSTOPPULLUP_WMIN
+  //#define ENDSTOPPULLUP_XMAX
+  //#define ENDSTOPPULLUP_YMAX
+  //#define ENDSTOPPULLUP_ZMAX
+  //#define ENDSTOPPULLUP_IMAX
+  //#define ENDSTOPPULLUP_JMAX
+  //#define ENDSTOPPULLUP_KMAX
+  //#define ENDSTOPPULLUP_UMAX
+  //#define ENDSTOPPULLUP_VMAX
+  //#define ENDSTOPPULLUP_WMAX
   //#define ENDSTOPPULLUP_ZMIN_PROBE
 #endif
 
@@ -650,12 +1073,24 @@
 //#define ENDSTOPPULLDOWNS
 #if DISABLED(ENDSTOPPULLDOWNS)
   // Disable ENDSTOPPULLDOWNS to set pulldowns individually
-  //#define ENDSTOPPULLDOWN_XMAX
-  //#define ENDSTOPPULLDOWN_YMAX
-  //#define ENDSTOPPULLDOWN_ZMAX
   //#define ENDSTOPPULLDOWN_XMIN
   //#define ENDSTOPPULLDOWN_YMIN
   //#define ENDSTOPPULLDOWN_ZMIN
+  //#define ENDSTOPPULLDOWN_IMIN
+  //#define ENDSTOPPULLDOWN_JMIN
+  //#define ENDSTOPPULLDOWN_KMIN
+  //#define ENDSTOPPULLDOWN_UMIN
+  //#define ENDSTOPPULLDOWN_VMIN
+  //#define ENDSTOPPULLDOWN_WMIN
+  //#define ENDSTOPPULLDOWN_XMAX
+  //#define ENDSTOPPULLDOWN_YMAX
+  //#define ENDSTOPPULLDOWN_ZMAX
+  //#define ENDSTOPPULLDOWN_IMAX
+  //#define ENDSTOPPULLDOWN_JMAX
+  //#define ENDSTOPPULLDOWN_KMAX
+  //#define ENDSTOPPULLDOWN_UMAX
+  //#define ENDSTOPPULLDOWN_VMAX
+  //#define ENDSTOPPULLDOWN_WMAX
   //#define ENDSTOPPULLDOWN_ZMIN_PROBE
 #endif
 
@@ -663,43 +1098,22 @@
 #define X_MIN_ENDSTOP_INVERTING true // Set to true to invert the logic of the endstop.
 #define Y_MIN_ENDSTOP_INVERTING true // Set to true to invert the logic of the endstop.
 #define Z_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define I_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define J_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define K_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define U_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define V_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define W_MIN_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
 #define X_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
 #define Y_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
 #define Z_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
-#define Z_MIN_PROBE_ENDSTOP_INVERTING true // Set to true to invert the logic of the probe.
-
-/**
- * Stepper Drivers
- *
- * These settings allow Marlin to tune stepper driver timing and enable advanced options for
- * stepper drivers that support them. You may also override timing options in Configuration_adv.h.
- *
- * A4988 is assumed for unspecified drivers.
- *
- * Options: A4988, A5984, DRV8825, LV8729, L6470, L6474, POWERSTEP01,
- *          TB6560, TB6600, TMC2100,
- *          TMC2130, TMC2130_STANDALONE, TMC2160, TMC2160_STANDALONE,
- *          TMC2208, TMC2208_STANDALONE, TMC2209, TMC2209_STANDALONE,
- *          TMC26X,  TMC26X_STANDALONE,  TMC2660, TMC2660_STANDALONE,
- *          TMC5130, TMC5130_STANDALONE, TMC5160, TMC5160_STANDALONE
- * :['A4988', 'A5984', 'DRV8825', 'LV8729', 'L6470', 'L6474', 'POWERSTEP01', 'TB6560', 'TB6600', 'TMC2100', 'TMC2130', 'TMC2130_STANDALONE', 'TMC2160', 'TMC2160_STANDALONE', 'TMC2208', 'TMC2208_STANDALONE', 'TMC2209', 'TMC2209_STANDALONE', 'TMC26X', 'TMC26X_STANDALONE', 'TMC2660', 'TMC2660_STANDALONE', 'TMC5130', 'TMC5130_STANDALONE', 'TMC5160', 'TMC5160_STANDALONE']
- */
-#define X_DRIVER_TYPE  TMC2130
-#define Y_DRIVER_TYPE  TMC2130
-#define Z_DRIVER_TYPE  TMC2130
-//#define X2_DRIVER_TYPE A4988
-//#define Y2_DRIVER_TYPE A4988
-#define Z2_DRIVER_TYPE TMC2130
-//#define Z3_DRIVER_TYPE A4988
-//#define Z4_DRIVER_TYPE A4988
-#define E0_DRIVER_TYPE TMC2130
-//#define E1_DRIVER_TYPE A4988
-//#define E2_DRIVER_TYPE A4988
-//#define E3_DRIVER_TYPE A4988
-//#define E4_DRIVER_TYPE A4988
-//#define E5_DRIVER_TYPE A4988
-//#define E6_DRIVER_TYPE A4988
-//#define E7_DRIVER_TYPE A4988
+#define I_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define J_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define K_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define U_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define V_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define W_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop.
+#define Z_MIN_PROBE_ENDSTOP_INVERTING false // Set to true to invert the logic of the probe.
 
 // Enable this feature if all enabled endstop pins are interrupt-capable.
 // This will remove the need to poll the interrupt pins, saving many CPU cycles.
@@ -743,16 +1157,16 @@
 //#define DISTINCT_E_FACTORS
 
 /**
- * Default Axis Steps Per Unit (steps/mm)
+ * Default Axis Steps Per Unit (linear=steps/mm, rotational=steps/°)
  * Override with M92
- *                                      X, Y, Z, E0 [, E1[, E2...]]
+ *                                      X, Y, Z [, I [, J [, K...]]], E0 [, E1[, E2...]]
  */
 #define DEFAULT_AXIS_STEPS_PER_UNIT   { 80, 80, 400, 100 }
 
 /**
- * Default Max Feed Rate (mm/s)
+ * Default Max Feed Rate (linear=mm/s, rotational=°/s)
  * Override with M203
- *                                      X, Y, Z, E0 [, E1[, E2...]]
+ *                                      X, Y, Z [, I [, J [, K...]]], E0 [, E1[, E2...]]
  */
 #define DEFAULT_MAX_FEEDRATE          { 200, 200, 8, 25 }
 
@@ -762,10 +1176,10 @@
 #endif
 
 /**
- * Default Max Acceleration (change/s) change = mm/s
+ * Default Max Acceleration (speed change with time) (linear=mm/(s^2), rotational=°/(s^2))
  * (Maximum start speed for accelerated moves)
  * Override with M201
- *                                      X, Y, Z, E0 [, E1[, E2...]]
+ *                                      X, Y, Z [, I [, J [, K...]]], E0 [, E1[, E2...]]
  */
 #define DEFAULT_MAX_ACCELERATION      { 800, 800, 100, 10000 }
 
@@ -775,7 +1189,7 @@
 #endif
 
 /**
- * Default Acceleration (change/s) change = mm/s
+ * Default Acceleration (speed change with time) (linear=mm/(s^2), rotational=°/(s^2))
  * Override with M204
  *
  *   M204 P    Acceleration
@@ -788,7 +1202,7 @@
 
 /**
  * Default Jerk limits (mm/s)
- * Override with M205 X Y Z E
+ * Override with M205 X Y Z . . . E
  *
  * "Jerk" specifies the minimum speed change that requires acceleration.
  * When changing speed and direction, if the difference is less than the
@@ -799,6 +1213,12 @@
   #define DEFAULT_XJERK  7.0
   #define DEFAULT_YJERK  7.0
   #define DEFAULT_ZJERK  0.3
+  //#define DEFAULT_IJERK  0.3
+  //#define DEFAULT_JJERK  0.3
+  //#define DEFAULT_KJERK  0.3
+  //#define DEFAULT_UJERK  0.3
+  //#define DEFAULT_VJERK  0.3
+  //#define DEFAULT_WJERK  0.3
 
   //#define TRAVEL_EXTRA_XYJERK 0.0     // Additional jerk allowance for all travel moves
 
@@ -882,7 +1302,6 @@
  * or (with LCD_BED_LEVELING) the LCD controller.
  */
 //#define PROBE_MANUALLY
-//#define MANUAL_PROBE_START_Z 0.2
 
 /**
  * A Fix-Mounted Probe either doesn't deploy or needs manual deployment.
@@ -908,9 +1327,15 @@
 #define BLTOUCH
 
 /**
- * Pressure sensor with a BLTouch-like interface
+ * MagLev V4 probe by MDD
+ *
+ * This probe is deployed and activated by powering a built-in electromagnet.
  */
-//#define CREALITY_TOUCH
+//#define MAGLEV4
+#if ENABLED(MAGLEV4)
+  //#define MAGLEV_TRIGGER_PIN 11     // Set to the connected digital output
+  #define MAGLEV_TRIGGER_DELAY 15     // Changing this risks overheating the coil
+#endif
 
 /**
  * Touch-MI Probe by hotends.fr
@@ -943,8 +1368,29 @@
   #define Z_PROBE_RETRACT_X X_MAX_POS
 #endif
 
+/**
+ * Magnetically Mounted Probe
+ * For probes such as Euclid, Klicky, Klackender, etc.
+ */
+//#define MAG_MOUNTED_PROBE
+#if ENABLED(MAG_MOUNTED_PROBE)
+  #define PROBE_DEPLOY_FEEDRATE (133*60)  // (mm/min) Probe deploy speed
+  #define PROBE_STOW_FEEDRATE   (133*60)  // (mm/min) Probe stow speed
+
+  #define MAG_MOUNTED_DEPLOY_1 { PROBE_DEPLOY_FEEDRATE, { 245, 114, 30 } }  // Move to side Dock & Attach probe
+  #define MAG_MOUNTED_DEPLOY_2 { PROBE_DEPLOY_FEEDRATE, { 210, 114, 30 } }  // Move probe off dock
+  #define MAG_MOUNTED_DEPLOY_3 { PROBE_DEPLOY_FEEDRATE, {   0,   0,  0 } }  // Extra move if needed
+  #define MAG_MOUNTED_DEPLOY_4 { PROBE_DEPLOY_FEEDRATE, {   0,   0,  0 } }  // Extra move if needed
+  #define MAG_MOUNTED_DEPLOY_5 { PROBE_DEPLOY_FEEDRATE, {   0,   0,  0 } }  // Extra move if needed
+  #define MAG_MOUNTED_STOW_1   { PROBE_STOW_FEEDRATE,   { 245, 114, 20 } }  // Move to dock
+  #define MAG_MOUNTED_STOW_2   { PROBE_STOW_FEEDRATE,   { 245, 114,  0 } }  // Place probe beside remover
+  #define MAG_MOUNTED_STOW_3   { PROBE_STOW_FEEDRATE,   { 230, 114,  0 } }  // Side move to remove probe
+  #define MAG_MOUNTED_STOW_4   { PROBE_STOW_FEEDRATE,   { 210, 114, 20 } }  // Side move to remove probe
+  #define MAG_MOUNTED_STOW_5   { PROBE_STOW_FEEDRATE,   {   0,   0,  0 } }  // Extra move if needed
+#endif
+
 // Duet Smart Effector (for delta printers) - https://bit.ly/2ul5U7J
-// When the pin is defined you can use M672 to set/reset the probe sensivity.
+// When the pin is defined you can use M672 to set/reset the probe sensitivity.
 //#define DUET_SMART_EFFECTOR
 #if ENABLED(DUET_SMART_EFFECTOR)
   #define SMART_EFFECTOR_MOD_PIN  -1  // Connect a GPIO pin to the Smart Effector MOD pin
@@ -958,17 +1404,55 @@
  */
 //#define SENSORLESS_PROBING
 
-//
-// For Z_PROBE_ALLEN_KEY see the Delta example configurations.
-//
+/**
+ * Allen key retractable z-probe as seen on many Kossel delta printers - https://reprap.org/wiki/Kossel#Automatic_bed_leveling_probe
+ * Deploys by touching z-axis belt. Retracts by pushing the probe down.
+ */
+//#define Z_PROBE_ALLEN_KEY
+#if ENABLED(Z_PROBE_ALLEN_KEY)
+  // 2 or 3 sets of coordinates for deploying and retracting the spring loaded touch probe on G29,
+  // if servo actuated touch probe is not defined. Uncomment as appropriate for your printer/probe.
+
+  #define Z_PROBE_ALLEN_KEY_DEPLOY_1 { 30.0, DELTA_PRINTABLE_RADIUS, 100.0 }
+  #define Z_PROBE_ALLEN_KEY_DEPLOY_1_FEEDRATE XY_PROBE_FEEDRATE
+
+  #define Z_PROBE_ALLEN_KEY_DEPLOY_2 { 0.0, DELTA_PRINTABLE_RADIUS, 100.0 }
+  #define Z_PROBE_ALLEN_KEY_DEPLOY_2_FEEDRATE (XY_PROBE_FEEDRATE)/10
+
+  #define Z_PROBE_ALLEN_KEY_DEPLOY_3 { 0.0, (DELTA_PRINTABLE_RADIUS) * 0.75, 100.0 }
+  #define Z_PROBE_ALLEN_KEY_DEPLOY_3_FEEDRATE XY_PROBE_FEEDRATE
+
+  #define Z_PROBE_ALLEN_KEY_STOW_1 { -64.0, 56.0, 23.0 } // Move the probe into position
+  #define Z_PROBE_ALLEN_KEY_STOW_1_FEEDRATE XY_PROBE_FEEDRATE
+
+  #define Z_PROBE_ALLEN_KEY_STOW_2 { -64.0, 56.0, 3.0 } // Push it down
+  #define Z_PROBE_ALLEN_KEY_STOW_2_FEEDRATE (XY_PROBE_FEEDRATE)/10
+
+  #define Z_PROBE_ALLEN_KEY_STOW_3 { -64.0, 56.0, 50.0 } // Move it up to clear
+  #define Z_PROBE_ALLEN_KEY_STOW_3_FEEDRATE XY_PROBE_FEEDRATE
+
+  #define Z_PROBE_ALLEN_KEY_STOW_4 { 0.0, 0.0, 50.0 }
+  #define Z_PROBE_ALLEN_KEY_STOW_4_FEEDRATE XY_PROBE_FEEDRATE
+
+#endif // Z_PROBE_ALLEN_KEY
 
 /**
  * Nozzle-to-Probe offsets { X, Y, Z }
  *
- * - Use a caliper or ruler to measure the distance from the tip of
+ * X and Y offset
+ *   Use a caliper or ruler to measure the distance from the tip of
  *   the Nozzle to the center-point of the Probe in the X and Y axes.
+ *
+ * Z offset
  * - For the Z offset use your best known value and adjust at runtime.
- * - Probe Offsets can be tuned at runtime with 'M851', LCD menus, babystepping, etc.
+ * - Common probes trigger below the nozzle and have negative values for Z offset.
+ * - Probes triggering above the nozzle height are uncommon but do exist. When using
+ *   probes such as this, carefully set Z_CLEARANCE_DEPLOY_PROBE and Z_CLEARANCE_BETWEEN_PROBES
+ *   to avoid collisions during probing.
+ *
+ * Tune and Adjust
+ * -  Probe Offsets can be tuned at runtime with 'M851', LCD menus, babystepping, etc.
+ * -  PROBE_OFFSET_WIZARD (configuration_adv.h) can be used for setting the Z offset.
  *
  * Assuming the typical work area orientation:
  *  - Probe to RIGHT of the Nozzle has a Positive X offset
@@ -999,13 +1483,49 @@
 #define PROBING_MARGIN 10
 
 // X and Y axis travel speed (mm/min) between probes
-#define XY_PROBE_SPEED (133*60)
+#define XY_PROBE_FEEDRATE (133*60)
 
 // Feedrate (mm/min) for the first approach when double-probing (MULTIPLE_PROBING == 2)
-#define Z_PROBE_SPEED_FAST HOMING_FEEDRATE_Z
+#define Z_PROBE_FEEDRATE_FAST (4*60)
 
 // Feedrate (mm/min) for the "accurate" probe of each point
-#define Z_PROBE_SPEED_SLOW (Z_PROBE_SPEED_FAST / 2)
+#define Z_PROBE_FEEDRATE_SLOW (Z_PROBE_FEEDRATE_FAST / 2)
+
+/**
+ * Probe Activation Switch
+ * A switch indicating proper deployment, or an optical
+ * switch triggered when the carriage is near the bed.
+ */
+//#define PROBE_ACTIVATION_SWITCH
+#if ENABLED(PROBE_ACTIVATION_SWITCH)
+  #define PROBE_ACTIVATION_SWITCH_STATE LOW // State indicating probe is active
+  //#define PROBE_ACTIVATION_SWITCH_PIN PC6 // Override default pin
+#endif
+
+/**
+ * Tare Probe (determine zero-point) prior to each probe.
+ * Useful for a strain gauge or piezo sensor that needs to factor out
+ * elements such as cables pulling on the carriage.
+ */
+//#define PROBE_TARE
+#if ENABLED(PROBE_TARE)
+  #define PROBE_TARE_TIME  200    // (ms) Time to hold tare pin
+  #define PROBE_TARE_DELAY 200    // (ms) Delay after tare before
+  #define PROBE_TARE_STATE HIGH   // State to write pin for tare
+  //#define PROBE_TARE_PIN PA5    // Override default pin
+  #if ENABLED(PROBE_ACTIVATION_SWITCH)
+    //#define PROBE_TARE_ONLY_WHILE_INACTIVE  // Fail to tare/probe if PROBE_ACTIVATION_SWITCH is active
+  #endif
+#endif
+
+/**
+ * Probe Enable / Disable
+ * The probe only provides a triggered signal when enabled.
+ */
+//#define PROBE_ENABLE_DISABLE
+#if ENABLED(PROBE_ENABLE_DISABLE)
+  //#define PROBE_ENABLE_PIN -1   // Override the default pin here
+#endif
 
 /**
  * Multiple Probing
@@ -1063,23 +1583,44 @@
 //#define PROBING_HEATERS_OFF       // Turn heaters off when probing
 #if ENABLED(PROBING_HEATERS_OFF)
   //#define WAIT_FOR_BED_HEATER     // Wait for bed to heat back up between probes (to improve accuracy)
+  //#define WAIT_FOR_HOTEND         // Wait for hotend to heat back up between probes (to improve accuracy & prevent cold extrude)
 #endif
 //#define PROBING_FANS_OFF          // Turn fans off when probing
-//#define PROBING_STEPPERS_OFF      // Turn steppers off (unless needed to hold position) when probing
+//#define PROBING_ESTEPPERS_OFF     // Turn all extruder steppers off when probing
+//#define PROBING_STEPPERS_OFF      // Turn all steppers off (unless needed to hold position) when probing (including extruders)
 //#define DELAY_BEFORE_PROBING 200  // (ms) To prevent vibrations from triggering piezo sensors
 
+// Require minimum nozzle and/or bed temperature for probing
+//#define PREHEAT_BEFORE_PROBING
+#if ENABLED(PREHEAT_BEFORE_PROBING)
+  #define PROBING_NOZZLE_TEMP 120   // (°C) Only applies to E0 at this time
+  #define PROBING_BED_TEMP     50
+#endif
+
 // For Inverting Stepper Enable Pins (Active Low) use 0, Non Inverting (Active High) use 1
 // :{ 0:'Low', 1:'High' }
 #define X_ENABLE_ON 0
 #define Y_ENABLE_ON 0
 #define Z_ENABLE_ON 0
 #define E_ENABLE_ON 0 // For all extruders
+//#define I_ENABLE_ON 0
+//#define J_ENABLE_ON 0
+//#define K_ENABLE_ON 0
+//#define U_ENABLE_ON 0
+//#define V_ENABLE_ON 0
+//#define W_ENABLE_ON 0
 
 // Disable axis steppers immediately when they're not being stepped.
 // WARNING: When motors turn off there is a chance of losing position accuracy!
 #define DISABLE_X false
 #define DISABLE_Y false
 #define DISABLE_Z false
+//#define DISABLE_I false
+//#define DISABLE_J false
+//#define DISABLE_K false
+//#define DISABLE_U false
+//#define DISABLE_V false
+//#define DISABLE_W false
 
 // Turn off the display blinking that warns about possible accuracy reduction
 //#define DISABLE_REDUCED_ACCURACY_WARNING
@@ -1089,12 +1630,18 @@
 #define DISABLE_E false             // Disable the extruder when not stepping
 #define DISABLE_INACTIVE_EXTRUDER   // Keep only the active extruder enabled
 
-// @section machine
+// @section motion
 
 // Invert the stepper direction. Change (or reverse the motor connector) if an axis goes the wrong way.
 #define INVERT_X_DIR true
 #define INVERT_Y_DIR true
 #define INVERT_Z_DIR false
+//#define INVERT_I_DIR false
+//#define INVERT_J_DIR false
+//#define INVERT_K_DIR false
+//#define INVERT_U_DIR false
+//#define INVERT_V_DIR false
+//#define INVERT_W_DIR false
 
 // @section extruder
 
@@ -1110,9 +1657,15 @@
 
 // @section homing
 
-//#define NO_MOTION_BEFORE_HOMING // Inhibit movement until all axes have been homed
+//#define NO_MOTION_BEFORE_HOMING // Inhibit movement until all axes have been homed. Also enable HOME_AFTER_DEACTIVATE for extra safety.
+//#define HOME_AFTER_DEACTIVATE   // Require rehoming after steppers are deactivated. Also enable NO_MOTION_BEFORE_HOMING for extra safety.
 
-//#define UNKNOWN_Z_NO_RAISE      // Don't raise Z (lower the bed) if Z is "unknown." For beds that fall when Z is powered off.
+/**
+ * Set Z_IDLE_HEIGHT if the Z-Axis moves on its own when steppers are disabled.
+ *  - Use a low value (i.e., Z_MIN_POS) if the nozzle falls down to the bed.
+ *  - Use a large value (i.e., Z_MAX_POS) if the bed falls down, away from the nozzle.
+ */
+//#define Z_IDLE_HEIGHT Z_HOME_POS
 
 //#define Z_HOMING_HEIGHT  4      // (mm) Minimal Z height before homing (G28) for Z clearance above the bed, clamps, ...
                                   // Be sure to have this much clearance over your Z_MAX_POS to prevent grinding.
@@ -1124,20 +1677,38 @@
 #define X_HOME_DIR -1
 #define Y_HOME_DIR -1
 #define Z_HOME_DIR -1
+//#define I_HOME_DIR -1
+//#define J_HOME_DIR -1
+//#define K_HOME_DIR -1
+//#define U_HOME_DIR -1
+//#define V_HOME_DIR -1
+//#define W_HOME_DIR -1
 
-// @section machine
+// @section geometry
 
-// The size of the print bed
+// The size of the printable area
 #define X_BED_SIZE 200
 #define Y_BED_SIZE 200
 
-// Travel limits (mm) after homing, corresponding to endstop positions.
+// Travel limits (linear=mm, rotational=°) after homing, corresponding to endstop positions.
 #define X_MIN_POS 0
 #define Y_MIN_POS 0
 #define Z_MIN_POS 0
 #define X_MAX_POS X_BED_SIZE
 #define Y_MAX_POS Y_BED_SIZE
 #define Z_MAX_POS 165
+//#define I_MIN_POS 0
+//#define I_MAX_POS 50
+//#define J_MIN_POS 0
+//#define J_MAX_POS 50
+//#define K_MIN_POS 0
+//#define K_MAX_POS 50
+//#define U_MIN_POS 0
+//#define U_MAX_POS 50
+//#define V_MIN_POS 0
+//#define V_MAX_POS 50
+//#define W_MIN_POS 0
+//#define W_MAX_POS 50
 
 /**
  * Software Endstops
@@ -1154,6 +1725,12 @@
   #define MIN_SOFTWARE_ENDSTOP_X
   #define MIN_SOFTWARE_ENDSTOP_Y
   #define MIN_SOFTWARE_ENDSTOP_Z
+  #define MIN_SOFTWARE_ENDSTOP_I
+  #define MIN_SOFTWARE_ENDSTOP_J
+  #define MIN_SOFTWARE_ENDSTOP_K
+  #define MIN_SOFTWARE_ENDSTOP_U
+  #define MIN_SOFTWARE_ENDSTOP_V
+  #define MIN_SOFTWARE_ENDSTOP_W
 #endif
 
 // Max software endstops constrain movement within maximum coordinate bounds
@@ -1162,6 +1739,12 @@
   #define MAX_SOFTWARE_ENDSTOP_X
   #define MAX_SOFTWARE_ENDSTOP_Y
   #define MAX_SOFTWARE_ENDSTOP_Z
+  #define MAX_SOFTWARE_ENDSTOP_I
+  #define MAX_SOFTWARE_ENDSTOP_J
+  #define MAX_SOFTWARE_ENDSTOP_K
+  #define MAX_SOFTWARE_ENDSTOP_U
+  #define MAX_SOFTWARE_ENDSTOP_V
+  #define MAX_SOFTWARE_ENDSTOP_W
 #endif
 
 #if EITHER(MIN_SOFTWARE_ENDSTOPS, MAX_SOFTWARE_ENDSTOPS)
@@ -1172,6 +1755,12 @@
  * Filament Runout Sensors
  * Mechanical or opto endstops are used to check for the presence of filament.
  *
+ * IMPORTANT: Runout will only trigger if Marlin is aware that a print job is running.
+ * Marlin knows a print job is running when:
+ *  1. Running a print job from media started with M24.
+ *  2. The Print Job Timer has been started with M75.
+ *  3. The heaters were turned on and PRINTJOB_TIMER_AUTOSTART is enabled.
+ *
  * RAMPS-based boards use SERVO3_PIN for the first runout sensor.
  * For other boards you may need to define FIL_RUNOUT_PIN, FIL_RUNOUT2_PIN, etc.
  */
@@ -1179,12 +1768,49 @@
 #if ENABLED(FILAMENT_RUNOUT_SENSOR)
   #define FIL_RUNOUT_ENABLED_DEFAULT true // Enable the sensor on startup. Override with M412 followed by M500.
   #define NUM_RUNOUT_SENSORS   1          // Number of sensors, up to one per extruder. Define a FIL_RUNOUT#_PIN for each.
+
   #define FIL_RUNOUT_STATE     LOW        // Pin state indicating that filament is NOT present.
   #define FIL_RUNOUT_PULLUP               // Use internal pullup for filament runout pins.
   //#define FIL_RUNOUT_PULLDOWN           // Use internal pulldown for filament runout pins.
+  //#define WATCH_ALL_RUNOUT_SENSORS      // Execute runout script on any triggering sensor, not only for the active extruder.
+                                          // This is automatically enabled for MIXING_EXTRUDERs.
 
-  // Set one or more commands to execute on filament runout.
-  // (After 'M412 H' Marlin will ask the host to handle the process.)
+  // Override individually if the runout sensors vary
+  //#define FIL_RUNOUT1_STATE LOW
+  //#define FIL_RUNOUT1_PULLUP
+  //#define FIL_RUNOUT1_PULLDOWN
+
+  //#define FIL_RUNOUT2_STATE LOW
+  //#define FIL_RUNOUT2_PULLUP
+  //#define FIL_RUNOUT2_PULLDOWN
+
+  //#define FIL_RUNOUT3_STATE LOW
+  //#define FIL_RUNOUT3_PULLUP
+  //#define FIL_RUNOUT3_PULLDOWN
+
+  //#define FIL_RUNOUT4_STATE LOW
+  //#define FIL_RUNOUT4_PULLUP
+  //#define FIL_RUNOUT4_PULLDOWN
+
+  //#define FIL_RUNOUT5_STATE LOW
+  //#define FIL_RUNOUT5_PULLUP
+  //#define FIL_RUNOUT5_PULLDOWN
+
+  //#define FIL_RUNOUT6_STATE LOW
+  //#define FIL_RUNOUT6_PULLUP
+  //#define FIL_RUNOUT6_PULLDOWN
+
+  //#define FIL_RUNOUT7_STATE LOW
+  //#define FIL_RUNOUT7_PULLUP
+  //#define FIL_RUNOUT7_PULLDOWN
+
+  //#define FIL_RUNOUT8_STATE LOW
+  //#define FIL_RUNOUT8_PULLUP
+  //#define FIL_RUNOUT8_PULLDOWN
+
+  // Commands to execute on filament runout.
+  // With multiple runout sensors use the %c placeholder for the current tool in commands (e.g., "M600 T%c")
+  // NOTE: After 'M412 H1' the host handles filament runout and this script does not apply.
   #define FILAMENT_RUNOUT_SCRIPT "M600"
 
   // After a runout is detected, continue printing this length of filament
@@ -1245,10 +1871,30 @@
 //#define MESH_BED_LEVELING
 
 /**
- * Normally G28 leaves leveling disabled on completion. Enable
- * this option to have G28 restore the prior leveling state.
+ * Normally G28 leaves leveling disabled on completion. Enable one of
+ * these options to restore the prior leveling state or to always enable
+ * leveling immediately after G28.
  */
 #define RESTORE_LEVELING_AFTER_G28
+//#define ENABLE_LEVELING_AFTER_G28
+
+/**
+ * Auto-leveling needs preheating
+ */
+#define PREHEAT_BEFORE_LEVELING
+#if ENABLED(PREHEAT_BEFORE_LEVELING)
+  #define LEVELING_NOZZLE_TEMP 120   // (°C) Only applies to E0 at this time
+  #define LEVELING_BED_TEMP     50
+#endif
+
+/**
+ * Bed Distance Sensor
+ *
+ * Measures the distance from bed to nozzle with accuracy of 0.01mm.
+ * For information about this sensor https://github.com/markniu/Bed_Distance_sensor
+ * Uses I2C port, so it requires I2C library markyue/Panda_SoftMasterI2C.
+ */
+//#define BD_SENSOR
 
 /**
  * Enable detailed logging of G28, G29, M48, etc.
@@ -1257,15 +1903,27 @@
  */
 //#define DEBUG_LEVELING_FEATURE
 
-#if ANY(MESH_BED_LEVELING, AUTO_BED_LEVELING_BILINEAR, AUTO_BED_LEVELING_UBL)
-  // Gradually reduce leveling correction until a set height is reached,
-  // at which point movement will be level to the machine's XY plane.
-  // The height can be set with M420 Z
-  #define ENABLE_LEVELING_FADE_HEIGHT
+#if ANY(MESH_BED_LEVELING, AUTO_BED_LEVELING_UBL, PROBE_MANUALLY)
+  // Set a height for the start of manual adjustment
+  #define MANUAL_PROBE_START_Z 0.2  // (mm) Comment out to use the last-measured height
+#endif
 
-  // For Cartesian machines, instead of dividing moves on mesh boundaries,
-  // split up moves into short segments like a Delta. This follows the
-  // contours of the bed more closely than edge-to-edge straight moves.
+#if ANY(MESH_BED_LEVELING, AUTO_BED_LEVELING_BILINEAR, AUTO_BED_LEVELING_UBL)
+  /**
+   * Gradually reduce leveling correction until a set height is reached,
+   * at which point movement will be level to the machine's XY plane.
+   * The height can be set with M420 Z
+   */
+  #define ENABLE_LEVELING_FADE_HEIGHT
+  #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
+    #define DEFAULT_LEVELING_FADE_HEIGHT 10.0 // (mm) Default fade height.
+  #endif
+
+  /**
+   * For Cartesian machines, instead of dividing moves on mesh boundaries,
+   * split up moves into short segments like a Delta. This follows the
+   * contours of the bed more closely than edge-to-edge straight moves.
+   */
   #define SEGMENT_LEVELED_MOVES
   #define LEVELED_SEGMENT_LENGTH 5.0 // (mm) Length of all segments (except the last one)
 
@@ -1275,10 +1933,11 @@
   #define G26_MESH_VALIDATION
   #if ENABLED(G26_MESH_VALIDATION)
     #define MESH_TEST_NOZZLE_SIZE    0.4  // (mm) Diameter of primary nozzle.
-    #define MESH_TEST_LAYER_HEIGHT   0.2  // (mm) Default layer height for the G26 Mesh Validation Tool.
-    #define MESH_TEST_HOTEND_TEMP  235   // (°C) Default nozzle temperature for the G26 Mesh Validation Tool.
-    #define MESH_TEST_BED_TEMP      80   // (°C) Default bed temperature for the G26 Mesh Validation Tool.
-    #define G26_XY_FEEDRATE         20    // (mm/s) Feedrate for XY Moves for the G26 Mesh Validation Tool.
+    #define MESH_TEST_LAYER_HEIGHT   0.2  // (mm) Default layer height for G26.
+    #define MESH_TEST_HOTEND_TEMP  220    // (°C) Default nozzle temperature for G26.
+    #define MESH_TEST_BED_TEMP      60    // (°C) Default bed temperature for G26.
+    #define G26_XY_FEEDRATE         20    // (mm/s) Feedrate for G26 XY moves.
+    #define G26_XY_FEEDRATE_TRAVEL 20    // (mm/s) Feedrate for G26 XY travel moves.
     #define G26_RETRACT_MULTIPLIER   1.0  // G26 Q (retraction) used by default between mesh test elements.
   #endif
 
@@ -1323,12 +1982,16 @@
   #define GRID_MAX_POINTS_X 10      // Don't use more than 15 points per axis, implementation limited.
   #define GRID_MAX_POINTS_Y GRID_MAX_POINTS_X
 
+  //#define UBL_HILBERT_CURVE       // Use Hilbert distribution for less travel when probing multiple points
+
   #define UBL_MESH_EDIT_MOVES_Z     // Sophisticated users prefer no movement of nozzle
   #define UBL_SAVE_ACTIVE_ON_M500   // Save the currently active mesh in the current slot on M500
 
   //#define UBL_Z_RAISE_WHEN_OFF_MESH 2.5 // When the nozzle is off the mesh, this value is used
                                           // as the Z-Height correction value.
 
+  //#define UBL_MESH_WIZARD         // Run several commands in a row to get a complete mesh
+
 #elif ENABLED(MESH_BED_LEVELING)
 
   //===========================================================================
@@ -1356,13 +2019,38 @@
 #endif
 
 // Add a menu item to move between bed corners for manual bed adjustment
-//#define LEVEL_BED_CORNERS
+//#define LCD_BED_TRAMMING
 
-#if ENABLED(LEVEL_BED_CORNERS)
-  #define LEVEL_CORNERS_INSET_LFRB { 30, 30, 30, 30 } // (mm) Left, Front, Right, Back insets
-  #define LEVEL_CORNERS_HEIGHT      0.0   // (mm) Z height of nozzle at leveling points
-  #define LEVEL_CORNERS_Z_HOP       4.0   // (mm) Z height of nozzle between leveling points
-  //#define LEVEL_CENTER_TOO              // Move to the center after the last corner
+#if ENABLED(LCD_BED_TRAMMING)
+  #define BED_TRAMMING_INSET_LFRB { 30, 30, 30, 30 } // (mm) Left, Front, Right, Back insets
+  #define BED_TRAMMING_HEIGHT      0.0        // (mm) Z height of nozzle at leveling points
+  #define BED_TRAMMING_Z_HOP       4.0        // (mm) Z height of nozzle between leveling points
+  //#define BED_TRAMMING_INCLUDE_CENTER       // Move to the center after the last corner
+  //#define BED_TRAMMING_USE_PROBE
+  #if ENABLED(BED_TRAMMING_USE_PROBE)
+    #define BED_TRAMMING_PROBE_TOLERANCE 0.1  // (mm)
+    #define BED_TRAMMING_VERIFY_RAISED        // After adjustment triggers the probe, re-probe to verify
+    //#define BED_TRAMMING_AUDIO_FEEDBACK
+  #endif
+
+  /**
+   * Corner Leveling Order
+   *
+   * Set 2 or 4 points. When 2 points are given, the 3rd is the center of the opposite edge.
+   *
+   *  LF  Left-Front    RF  Right-Front
+   *  LB  Left-Back     RB  Right-Back
+   *
+   * Examples:
+   *
+   *      Default        {LF,RB,LB,RF}         {LF,RF}           {LB,LF}
+   *  LB --------- RB   LB --------- RB    LB --------- RB   LB --------- RB
+   *  |  4       3  |   | 3         2 |    |     <3>     |   | 1           |
+   *  |             |   |             |    |             |   |          <3>|
+   *  |  1       2  |   | 1         4 |    | 1         2 |   | 2           |
+   *  LF --------- RF   LF --------- RF    LF --------- RF   LF --------- RF
+   */
+  #define BED_TRAMMING_LEVELING_ORDER { LF, RF, RB, LB }
 #endif
 
 /**
@@ -1381,16 +2069,20 @@
 //#define MANUAL_X_HOME_POS 0
 //#define MANUAL_Y_HOME_POS 0
 //#define MANUAL_Z_HOME_POS 0
+//#define MANUAL_I_HOME_POS 0
+//#define MANUAL_J_HOME_POS 0
+//#define MANUAL_K_HOME_POS 0
+//#define MANUAL_U_HOME_POS 0
+//#define MANUAL_V_HOME_POS 0
+//#define MANUAL_W_HOME_POS 0
 
-// Use "Z Safe Homing" to avoid homing with a Z probe outside the bed area.
-//
-// With this feature enabled:
-//
-// - Allow Z homing only after X and Y homing AND stepper drivers still enabled.
-// - If stepper drivers time out, it will need X and Y homing again before Z homing.
-// - Move the Z probe (or nozzle) to a defined XY point before Z Homing.
-// - Prevent Z homing when the Z probe is outside bed area.
-//
+/**
+ * Use "Z Safe Homing" to avoid homing with a Z probe outside the bed area.
+ *
+ * - Moves the Z probe (or nozzle) to a defined XY point before Z homing.
+ * - Allows Z homing only when XY positions are known and trusted.
+ * - If stepper drivers sleep, XY homing may be required again before Z homing.
+ */
 #define Z_SAFE_HOMING
 
 #if ENABLED(Z_SAFE_HOMING)
@@ -1398,9 +2090,8 @@
   #define Z_SAFE_HOMING_Y_POINT Y_CENTER  // Y point for Z homing
 #endif
 
-// Homing speeds (mm/min)
-#define HOMING_FEEDRATE_XY (50*60)
-#define HOMING_FEEDRATE_Z  (4*60)
+// Homing speeds (linear=mm/min, rotational=°/min)
+#define HOMING_FEEDRATE_MM_M { (50*60), (50*60), (4*60) }
 
 // Validate that endstops are triggered on homing moves
 #define VALIDATE_HOMING_ENDSTOPS
@@ -1443,9 +2134,8 @@
   #define XY_DIAG_BD 282.8427124746
   #define XY_SIDE_AD 200
 
-  // Or, set the default skew factors directly here
-  // to override the above measurements:
-  #define XY_SKEW_FACTOR 0.0
+  // Or, set the XY skew factor directly:
+  //#define XY_SKEW_FACTOR 0.0
 
   //#define SKEW_CORRECTION_FOR_Z
   #if ENABLED(SKEW_CORRECTION_FOR_Z)
@@ -1454,8 +2144,10 @@
     #define YZ_DIAG_AC 282.8427124746
     #define YZ_DIAG_BD 282.8427124746
     #define YZ_SIDE_AD 200
-    #define XZ_SKEW_FACTOR 0.0
-    #define YZ_SKEW_FACTOR 0.0
+
+    // Or, set the Z skew factors directly:
+    //#define XZ_SKEW_FACTOR 0.0
+    //#define YZ_SKEW_FACTOR 0.0
   #endif
 
   // Enable this option for M852 to set skew at runtime
@@ -1466,7 +2158,7 @@
 //============================= Additional Features ===========================
 //=============================================================================
 
-// @section extras
+// @section eeprom
 
 /**
  * EEPROM
@@ -1480,10 +2172,14 @@
 #define EEPROM_SETTINGS     // Persistent storage with M500 and M501
 //#define DISABLE_M503        // Saves ~2700 bytes of PROGMEM. Disable for release!
 #define EEPROM_CHITCHAT       // Give feedback on EEPROM commands. Disable to save PROGMEM.
+#define EEPROM_BOOT_SILENT    // Keep M503 quiet and only give errors during first load
 #if ENABLED(EEPROM_SETTINGS)
   //#define EEPROM_AUTO_INIT  // Init EEPROM automatically on any errors.
+  //#define EEPROM_INIT_NOW   // Init EEPROM on first boot after a new build.
 #endif
 
+// @section host
+
 //
 // Host Keepalive
 //
@@ -1494,6 +2190,8 @@
 #define DEFAULT_KEEPALIVE_INTERVAL 2  // Number of seconds between "busy" messages. Set with M113.
 #define BUSY_WHILE_HEATING            // Some hosts require "busy" messages even during heating
 
+// @section units
+
 //
 // G20/G21 Inch mode support
 //
@@ -1506,17 +2204,23 @@
 
 // @section temperature
 
-// Preheat Constants
+//
+// Preheat Constants - Up to 10 are supported without changes
+//
 #define PREHEAT_1_LABEL       "PLA"
 #define PREHEAT_1_TEMP_HOTEND 205
 #define PREHEAT_1_TEMP_BED     60
+#define PREHEAT_1_TEMP_CHAMBER 35
 #define PREHEAT_1_FAN_SPEED     0 // Value from 0 to 255
 
-#define PREHEAT_2_LABEL       "PETG"
+#define PREHEAT_2_LABEL       "ABS"
 #define PREHEAT_2_TEMP_HOTEND 230
-#define PREHEAT_2_TEMP_BED    78
+#define PREHEAT_2_TEMP_BED    80
+#define PREHEAT_2_TEMP_CHAMBER 35
 #define PREHEAT_2_FAN_SPEED     0 // Value from 0 to 255
 
+// @section motion
+
 /**
  * Nozzle Park
  *
@@ -1533,8 +2237,7 @@
 #if ENABLED(NOZZLE_PARK_FEATURE)
   // Specify a park position as { X, Y, Z_raise }
   #define NOZZLE_PARK_POINT { (X_MIN_POS + 10), (Y_MAX_POS - 10), 20 }
-  //#define NOZZLE_PARK_X_ONLY          // X move only is required to park
-  //#define NOZZLE_PARK_Y_ONLY          // Y move only is required to park
+  #define NOZZLE_PARK_MOVE          0   // Park motion: 0 = XY Move, 1 = X Only, 2 = Y Only, 3 = X before Y, 4 = Y before X
   #define NOZZLE_PARK_Z_RAISE_MIN   2   // (mm) Always raise Z by at least this distance
   #define NOZZLE_PARK_XY_FEEDRATE 80    // (mm/s) X and Y axes feedrate (also used for delta Z axis)
   #define NOZZLE_PARK_Z_FEEDRATE    5   // (mm/s) Z axis feedrate (not used for delta printers)
@@ -1607,19 +2310,34 @@
   // For a purge/clean station mounted on the X axis
   //#define NOZZLE_CLEAN_NO_Y
 
+  // Require a minimum hotend temperature for cleaning
+  #define NOZZLE_CLEAN_MIN_TEMP 170
+  //#define NOZZLE_CLEAN_HEATUP       // Heat up the nozzle instead of skipping wipe
+
   // Explicit wipe G-code script applies to a G12 with no arguments.
   //#define WIPE_SEQUENCE_COMMANDS "G1 X-17 Y25 Z10 F4000\nG1 Z1\nM114\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 X-17 Y25\nG1 X-17 Y95\nG1 Z15\nM400\nG0 X-10.0 Y-9.0"
 
 #endif
 
+// @section host
+
 /**
  * Print Job Timer
  *
- * Automatically start and stop the print job timer on M104/M109/M190.
+ * Automatically start and stop the print job timer on M104/M109/M140/M190/M141/M191.
+ * The print job timer will only be stopped if the bed/chamber target temp is
+ * below BED_MINTEMP/CHAMBER_MINTEMP.
  *
- *   M104 (hotend, no wait) - high temp = none,        low temp = stop timer
- *   M109 (hotend, wait)    - high temp = start timer, low temp = stop timer
- *   M190 (bed, wait)       - high temp = start timer, low temp = none
+ *   M104 (hotend, no wait)  - high temp = none,        low temp = stop timer
+ *   M109 (hotend, wait)     - high temp = start timer, low temp = stop timer
+ *   M140 (bed, no wait)     - high temp = none,        low temp = stop timer
+ *   M190 (bed, wait)        - high temp = start timer, low temp = none
+ *   M141 (chamber, no wait) - high temp = none,        low temp = stop timer
+ *   M191 (chamber, wait)    - high temp = start timer, low temp = none
+ *
+ * For M104/M109, high temp is anything over EXTRUDE_MINTEMP / 2.
+ * For M140/M190, high temp is anything over BED_MINTEMP.
+ * For M141/M191, high temp is anything over CHAMBER_MINTEMP.
  *
  * The timer can also be controlled with the following commands:
  *
@@ -1629,6 +2347,8 @@
  */
 #define PRINTJOB_TIMER_AUTOSTART
 
+// @section stats
+
 /**
  * Print Counter
  *
@@ -1642,6 +2362,11 @@
  * View the current statistics with M78.
  */
 #define PRINTCOUNTER
+#if ENABLED(PRINTCOUNTER)
+  #define PRINTCOUNTER_SAVE_INTERVAL 60 // (minutes) EEPROM save interval during print. A value of 0 will save stats at end of print.
+#endif
+
+// @section security
 
 /**
  * Password
@@ -1678,17 +2403,17 @@
 //============================= LCD and SD support ============================
 //=============================================================================
 
-// @section lcd
+// @section interface
 
 /**
  * LCD LANGUAGE
  *
  * Select the language to display on the LCD. These languages are available:
  *
- *   en, an, bg, ca, cz, da, de, el, el_gr, es, eu, fi, fr, gl, hr, hu, it,
- *   jp_kana, ko_KR, nl, pl, pt, pt_br, ro, ru, sk, tr, uk, vi, zh_CN, zh_TW, test
+ *   en, an, bg, ca, cz, da, de, el, el_CY, es, eu, fi, fr, gl, hr, hu, it,
+ *   jp_kana, ko_KR, nl, pl, pt, pt_br, ro, ru, sk, sv, tr, uk, vi, zh_CN, zh_TW
  *
- * :{ 'en':'English', 'an':'Aragonese', 'bg':'Bulgarian', 'ca':'Catalan', 'cz':'Czech', 'da':'Danish', 'de':'German', 'el':'Greek', 'el_gr':'Greek (Greece)', 'es':'Spanish', 'eu':'Basque-Euskera', 'fi':'Finnish', 'fr':'French', 'gl':'Galician', 'hr':'Croatian', 'hu':'Hungarian', 'it':'Italian', 'jp_kana':'Japanese', 'ko_KR':'Korean (South Korea)', 'nl':'Dutch', 'pl':'Polish', 'pt':'Portuguese', 'pt_br':'Portuguese (Brazilian)', 'ro':'Romanian', 'ru':'Russian', 'sk':'Slovak', 'tr':'Turkish', 'uk':'Ukrainian', 'vi':'Vietnamese', 'zh_CN':'Chinese (Simplified)', 'zh_TW':'Chinese (Traditional)', 'test':'TEST' }
+ * :{ 'en':'English', 'an':'Aragonese', 'bg':'Bulgarian', 'ca':'Catalan', 'cz':'Czech', 'da':'Danish', 'de':'German', 'el':'Greek (Greece)', 'el_CY':'Greek (Cyprus)', 'es':'Spanish', 'eu':'Basque-Euskera', 'fi':'Finnish', 'fr':'French', 'gl':'Galician', 'hr':'Croatian', 'hu':'Hungarian', 'it':'Italian', 'jp_kana':'Japanese', 'ko_KR':'Korean (South Korea)', 'nl':'Dutch', 'pl':'Polish', 'pt':'Portuguese', 'pt_br':'Portuguese (Brazilian)', 'ro':'Romanian', 'ru':'Russian', 'sk':'Slovak', 'sv':'Swedish', 'tr':'Turkish', 'uk':'Ukrainian', 'vi':'Vietnamese', 'zh_CN':'Chinese (Simplified)', 'zh_TW':'Chinese (Traditional)' }
  */
 #define LCD_LANGUAGE en
 
@@ -1731,16 +2456,6 @@
  */
 #define SDSUPPORT
 
-/**
- * SD CARD: SPI SPEED
- *
- * Enable one of the following items for a slower SPI transfer speed.
- * This may be required to resolve "volume init" errors.
- */
-//#define SPI_SPEED SPI_HALF_SPEED
-//#define SPI_SPEED SPI_QUARTER_SPEED
-//#define SPI_SPEED SPI_EIGHTH_SPEED
-
 /**
  * SD CARD: ENABLE CRC
  *
@@ -1804,12 +2519,23 @@
 //
 //#define REVERSE_SELECT_DIRECTION
 
+//
+// Encoder EMI Noise Filter
+//
+// This option increases encoder samples to filter out phantom encoder clicks caused by EMI noise.
+//
+//#define ENCODER_NOISE_FILTER
+#if ENABLED(ENCODER_NOISE_FILTER)
+  #define ENCODER_SAMPLES 10
+#endif
+
 //
 // Individual Axis Homing
 //
 // Add individual axis homing items (Home X, Home Y, and Home Z) to the LCD menu.
 //
 #define INDIVIDUAL_AXIS_HOMING_MENU
+//#define INDIVIDUAL_AXIS_HOMING_SUBMENU
 
 //
 // SPEAKER/BUZZER
@@ -1833,6 +2559,7 @@
 //======================== LCD / Controller Selection =========================
 //========================   (Character-based LCDs)   =========================
 //=============================================================================
+// @section lcd
 
 //
 // RepRapDiscount Smart Controller.
@@ -1842,6 +2569,14 @@
 //
 //#define REPRAP_DISCOUNT_SMART_CONTROLLER
 
+//
+// GT2560 (YHCB2004) LCD Display
+//
+// Requires Testato, Koepel softwarewire library and
+// Andriy Golovnya's LiquidCrystal_AIP31068 library.
+//
+//#define YHCB2004
+
 //
 // Original RADDS LCD Display+Encoder+SDCardReader
 // http://doku.radds.org/dokumentation/lcd-display/
@@ -1992,9 +2727,14 @@
 //
 #define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
 
+//
+// K.3D Full Graphic Smart Controller
+//
+//#define K3D_FULL_GRAPHIC_SMART_CONTROLLER
+
 //
 // ReprapWorld Graphical LCD
-// https://reprapworld.com/?products_details&products_id/1218
+// https://reprapworld.com/electronics/3d-printer-modules/autonomous-printing/graphical-lcd-screen-v1-0/
 //
 //#define REPRAPWORLD_GRAPHICAL_LCD
 
@@ -2006,6 +2746,11 @@
 //#define VIKI2
 //#define miniVIKI
 
+//
+// Alfawise Ex8 printer LCD marked as WYH L12864 COG
+//
+//#define WYH_L12864
+
 //
 // MakerLab Mini Panel with graphic
 // controller and SD support - https://reprap.org/wiki/Mini_panel
@@ -2057,11 +2802,17 @@
 //
 //#define MKS_MINI_12864
 
+//
+// MKS MINI12864 V3 is an alias for FYSETC_MINI_12864_2_1. Type A/B. NeoPixel RGB Backlight.
+//
+//#define MKS_MINI_12864_V3
+
 //
 // MKS LCD12864A/B with graphic controller and SD support. Follows MKS_MINI_12864 pinout.
 // https://www.aliexpress.com/item/33018110072.html
 //
-//#define MKS_LCD12864
+//#define MKS_LCD12864A
+//#define MKS_LCD12864B
 
 //
 // FYSETC variant of the MINI12864 graphic controller with SD support
@@ -2073,6 +2824,11 @@
 //#define FYSETC_MINI_12864_2_1    // Type A/B. NeoPixel RGB Backlight
 //#define FYSETC_GENERIC_12864_1_1 // Larger display with basic ON/OFF backlight.
 
+//
+// BigTreeTech Mini 12864 V1.0 is an alias for FYSETC_MINI_12864_2_1. Type A/B. NeoPixel RGB Backlight.
+//
+//#define BTT_MINI_12864_V1
+
 //
 // Factory display for Creality CR-10
 // https://www.aliexpress.com/item/32833148327.html
@@ -2092,9 +2848,10 @@
 //
 // Anet 128x64 full graphics lcd with rotary encoder as used on Anet A6
 // A clone of the RepRapDiscount full graphics display but with
-// different pins/wiring (see pins_ANET_10.h).
+// different pins/wiring (see pins_ANET_10.h). Enable one of these.
 //
 //#define ANET_FULL_GRAPHICS_LCD
+//#define ANET_FULL_GRAPHICS_LCD_ALT_WIRING
 
 //
 // AZSMZ 12864 LCD with SD
@@ -2108,6 +2865,12 @@
 //
 //#define SILVER_GATE_GLCD_CONTROLLER
 
+//
+// eMotion Tech LCD with SD
+// https://www.reprap-france.com/produit/1234568748-ecran-graphique-128-x-64-points-2-1
+//
+//#define EMOTION_TECH_LCD
+
 //=============================================================================
 //==============================  OLED Displays  ==============================
 //=============================================================================
@@ -2132,7 +2895,7 @@
 //#define OLED_PANEL_TINYBOY2
 
 //
-// MKS OLED 1.3" 128×64 FULL GRAPHICS CONTROLLER
+// MKS OLED 1.3" 128×64 Full Graphics Controller
 // https://reprap.org/wiki/MKS_12864OLED
 //
 // Tiny, but very sharp OLED display
@@ -2141,7 +2904,7 @@
 //#define MKS_12864OLED_SSD1306  // Uses the SSD1306 controller
 
 //
-// Zonestar OLED 128×64 FULL GRAPHICS CONTROLLER
+// Zonestar OLED 128×64 Full Graphics Controller
 //
 //#define ZONESTAR_12864LCD           // Graphical (DOGM) with ST7920 controller
 //#define ZONESTAR_12864OLED          // 1.3" OLED with SH1106 controller (default)
@@ -2158,30 +2921,64 @@
 //#define OVERLORD_OLED
 
 //
-// FYSETC OLED 2.42" 128×64 FULL GRAPHICS CONTROLLER with WS2812 RGB
+// FYSETC OLED 2.42" 128×64 Full Graphics Controller with WS2812 RGB
 // Where to find : https://www.aliexpress.com/item/4000345255731.html
 //#define FYSETC_242_OLED_12864   // Uses the SSD1309 controller
 
+//
+// K.3D SSD1309 OLED 2.42" 128×64 Full Graphics Controller
+//
+//#define K3D_242_OLED_CONTROLLER   // Software SPI
+
 //=============================================================================
 //========================== Extensible UI Displays ===========================
 //=============================================================================
 
-//
-// DGUS Touch Display with DWIN OS. (Choose one.)
-// ORIGIN : https://www.aliexpress.com/item/32993409517.html
-// FYSETC : https://www.aliexpress.com/item/32961471929.html
-//
+/**
+ * DGUS Touch Display with DWIN OS. (Choose one.)
+ * ORIGIN : https://www.aliexpress.com/item/32993409517.html
+ * FYSETC : https://www.aliexpress.com/item/32961471929.html
+ * MKS    : https://www.aliexpress.com/item/1005002008179262.html
+ *
+ * Flash display with DGUS Displays for Marlin:
+ *  - Format the SD card to FAT32 with an allocation size of 4kb.
+ *  - Download files as specified for your type of display.
+ *  - Plug the microSD card into the back of the display.
+ *  - Boot the display and wait for the update to complete.
+ *
+ * ORIGIN (Marlin DWIN_SET)
+ *  - Download https://github.com/coldtobi/Marlin_DGUS_Resources
+ *  - Copy the downloaded DWIN_SET folder to the SD card.
+ *
+ * FYSETC (Supplier default)
+ *  - Download https://github.com/FYSETC/FYSTLCD-2.0
+ *  - Copy the downloaded SCREEN folder to the SD card.
+ *
+ * HIPRECY (Supplier default)
+ *  - Download https://github.com/HiPrecy/Touch-Lcd-LEO
+ *  - Copy the downloaded DWIN_SET folder to the SD card.
+ *
+ * MKS (MKS-H43) (Supplier default)
+ *  - Download https://github.com/makerbase-mks/MKS-H43
+ *  - Copy the downloaded DWIN_SET folder to the SD card.
+ *
+ * RELOADED (T5UID1)
+ *  - Download https://github.com/Desuuuu/DGUS-reloaded/releases
+ *  - Copy the downloaded DWIN_SET folder to the SD card.
+ */
 //#define DGUS_LCD_UI_ORIGIN
 //#define DGUS_LCD_UI_FYSETC
 //#define DGUS_LCD_UI_HIPRECY
+//#define DGUS_LCD_UI_MKS
+//#define DGUS_LCD_UI_RELOADED
+#if ENABLED(DGUS_LCD_UI_MKS)
+  #define USE_MKS_GREEN_UI
+#endif
 
 //
 // Touch-screen LCD for Malyan M200/M300 printers
 //
 //#define MALYAN_LCD
-#if ENABLED(MALYAN_LCD)
-  #define LCD_SERIAL_PORT 1  // Default is 1 for Malyan M200
-#endif
 
 //
 // Touch UI for FTDI EVE (FT800/FT810) displays
@@ -2195,13 +2992,18 @@
 //#define ANYCUBIC_LCD_I3MEGA
 //#define ANYCUBIC_LCD_CHIRON
 #if EITHER(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON)
-  #define LCD_SERIAL_PORT 3  // Default is 3 for Anycubic
   //#define ANYCUBIC_LCD_DEBUG
+  //#define ANYCUBIC_LCD_GCODE_EXT  // Add ".gcode" to menu entries for DGUS clone compatibility
 #endif
 
+//
+// 320x240 Nextion 2.8" serial TFT Resistive Touch Screen NX3224T028
+//
+//#define NEXTION_TFT
+
 //
 // Third-party or vendor-customized controller interfaces.
-// Sources should be installed in 'src/lcd/extensible_ui'.
+// Sources should be installed in 'src/lcd/extui'.
 //
 //#define EXTENSIBLE_UI
 
@@ -2214,22 +3016,104 @@
 //=============================================================================
 
 /**
- * TFT Type - Select your Display type
- *
- * Available options are:
- *   MKS_TS35_V2_0,
- *   MKS_ROBIN_TFT24, MKS_ROBIN_TFT28, MKS_ROBIN_TFT32, MKS_ROBIN_TFT35,
- *   MKS_ROBIN_TFT43, MKS_ROBIN_TFT_V1_1R
- *   TFT_TRONXY_X5SA, ANYCUBIC_TFT35, LONGER_LK_TFT28
- *   TFT_GENERIC
- *
- * For TFT_GENERIC, you need to configure these 3 options:
- *   Driver:     TFT_DRIVER
- *               Current Drivers are: AUTO, ST7735, ST7789, ST7796, R61505, ILI9328, ILI9341, ILI9488
- *   Resolution: TFT_WIDTH and TFT_HEIGHT
- *   Interface:  TFT_INTERFACE_FSMC or TFT_INTERFACE_SPI
+ * Specific TFT Model Presets. Enable one of the following options
+ * or enable TFT_GENERIC and set sub-options.
  */
+
+//
+// 480x320, 3.5", SPI Display with Rotary Encoder from MKS
+// Usually paired with MKS Robin Nano V2 & V3
+//
+//#define MKS_TS35_V2_0
+
+//
+// 320x240, 2.4", FSMC Display From MKS
+// Usually paired with MKS Robin Nano V1.2
+//
+//#define MKS_ROBIN_TFT24
+
+//
+// 320x240, 2.8", FSMC Display From MKS
+// Usually paired with MKS Robin Nano V1.2
+//
+//#define MKS_ROBIN_TFT28
+
+//
+// 320x240, 3.2", FSMC Display From MKS
+// Usually paired with MKS Robin Nano V1.2
+//
+//#define MKS_ROBIN_TFT32
+
+//
+// 480x320, 3.5", FSMC Display From MKS
+// Usually paired with MKS Robin Nano V1.2
+//
+//#define MKS_ROBIN_TFT35
+
+//
+// 480x272, 4.3", FSMC Display From MKS
+//
+//#define MKS_ROBIN_TFT43
+
+//
+// 320x240, 3.2", FSMC Display From MKS
+// Usually paired with MKS Robin
+//
+//#define MKS_ROBIN_TFT_V1_1R
+
+//
+// 480x320, 3.5", FSMC Stock Display from Tronxy
+//
+//#define TFT_TRONXY_X5SA
+
+//
+// 480x320, 3.5", FSMC Stock Display from AnyCubic
+//
+//#define ANYCUBIC_TFT35
+
+//
+// 320x240, 2.8", FSMC Stock Display from Longer/Alfawise
+//
+//#define LONGER_LK_TFT28
+
+//
+// 320x240, 2.8", FSMC Stock Display from ET4
+//
+//#define ANET_ET4_TFT28
+
+//
+// 480x320, 3.5", FSMC Stock Display from ET5
+//
+//#define ANET_ET5_TFT35
+
+//
+// 1024x600, 7", RGB Stock Display with Rotary Encoder from BIQU-BX
+//
+//#define BIQU_BX_TFT70
+
+//
+// 480x320, 3.5", SPI Stock Display with Rotary Encoder from BIQU B1 SE Series
+//
+//#define BTT_TFT35_SPI_V1_0
+
+//
+// Generic TFT with detailed options
+//
 //#define TFT_GENERIC
+#if ENABLED(TFT_GENERIC)
+  // :[ 'AUTO', 'ST7735', 'ST7789', 'ST7796', 'R61505', 'ILI9328', 'ILI9341', 'ILI9488' ]
+  #define TFT_DRIVER AUTO
+
+  // Interface. Enable one of the following options:
+  //#define TFT_INTERFACE_FSMC
+  //#define TFT_INTERFACE_SPI
+
+  // TFT Resolution. Enable one of the following options:
+  //#define TFT_RES_320x240
+  //#define TFT_RES_480x272
+  //#define TFT_RES_480x320
+  //#define TFT_RES_1024x600
+#endif
 
 /**
  * TFT UI - User Interface Selection. Enable one of the following options:
@@ -2245,6 +3129,14 @@
 //#define TFT_COLOR_UI
 //#define TFT_LVGL_UI
 
+#if ENABLED(TFT_COLOR_UI)
+  //#define TFT_SHARED_SPI   // SPI is shared between TFT display and other devices. Disable async data transfer
+#endif
+
+#if ENABLED(TFT_LVGL_UI)
+  //#define MKS_WIFI_MODULE  // MKS WiFi module
+#endif
+
 /**
  * TFT Rotation. Set to one of the following values:
  *
@@ -2262,22 +3154,38 @@
 //
 // Ender-3 v2 OEM display. A DWIN display with Rotary Encoder.
 //
-//#define DWIN_CREALITY_LCD
+//#define DWIN_CREALITY_LCD           // Creality UI
+//#define DWIN_LCD_PROUI              // Pro UI by MRiscoC
+//#define DWIN_CREALITY_LCD_JYERSUI   // Jyers UI by Jacob Myers
+//#define DWIN_MARLINUI_PORTRAIT      // MarlinUI (portrait orientation)
+//#define DWIN_MARLINUI_LANDSCAPE     // MarlinUI (landscape orientation)
 
 //
-// ADS7843/XPT2046 ADC Touchscreen such as ILI9341 2.8
+// Touch Screen Settings
 //
 //#define TOUCH_SCREEN
 #if ENABLED(TOUCH_SCREEN)
-  #define BUTTON_DELAY_EDIT  50 // (ms) Button repeat delay for edit screens
-  #define BUTTON_DELAY_MENU 250 // (ms) Button repeat delay for menus
+  #define BUTTON_DELAY_EDIT      50 // (ms) Button repeat delay for edit screens
+  #define BUTTON_DELAY_MENU     250 // (ms) Button repeat delay for menus
+
+  //#define DISABLE_ENCODER         // Disable the click encoder, if any
+  //#define TOUCH_IDLE_SLEEP_MINS 5 // (minutes) Display Sleep after a period of inactivity. Set with M255 S.
 
   #define TOUCH_SCREEN_CALIBRATION
 
-  //#define XPT2046_X_CALIBRATION 12316
-  //#define XPT2046_Y_CALIBRATION -8981
-  //#define XPT2046_X_OFFSET        -43
-  //#define XPT2046_Y_OFFSET        257
+  //#define TOUCH_CALIBRATION_X 12316
+  //#define TOUCH_CALIBRATION_Y -8981
+  //#define TOUCH_OFFSET_X        -43
+  //#define TOUCH_OFFSET_Y        257
+  //#define TOUCH_ORIENTATION TOUCH_LANDSCAPE
+
+  #if BOTH(TOUCH_SCREEN_CALIBRATION, EEPROM_SETTINGS)
+    #define TOUCH_CALIBRATION_AUTO_SAVE // Auto save successful calibration values to EEPROM
+  #endif
+
+  #if ENABLED(TFT_COLOR_UI)
+    //#define SINGLE_TOUCH_NAVIGATION
+  #endif
 #endif
 
 //
@@ -2287,19 +3195,21 @@
 //#define REPRAPWORLD_KEYPAD
 //#define REPRAPWORLD_KEYPAD_MOVE_STEP 10.0 // (mm) Distance to move per key-press
 
+//
+// EasyThreeD ET-4000+ with button input and status LED
+//
+//#define EASYTHREED_UI
+
 //=============================================================================
 //=============================== Extra Features ==============================
 //=============================================================================
 
-// @section extras
+// @section fans
 
 // Set number of user-controlled fans. Disable to use all board-defined fans.
 // :[1,2,3,4,5,6,7,8]
 //#define NUM_M106_FANS 1
 
-// Increase the FAN PWM frequency. Removes the PWM noise but increases heating in the FET/Arduino
-//#define FAST_PWM_FAN
-
 // Use software PWM to drive the fan, as for the heaters. This uses a very low frequency
 // which is not as annoying as with the hardware PWM. On the other hand, if this frequency
 // is too low, you should also increment SOFT_PWM_SCALE.
@@ -2318,14 +3228,18 @@
 // duty cycle is attained.
 //#define SOFT_PWM_DITHER
 
+// @section extras
+
+// Support for the BariCUDA Paste Extruder
+//#define BARICUDA
+
+// @section lights
+
 // Temperature status LEDs that display the hotend and bed temperature.
 // If all hotends, bed temperature, and target temperature are under 54C
 // then the BLUE led is on. Otherwise the RED led is on. (1C hysteresis)
 //#define TEMP_STAT_LEDS
 
-// Support for the BariCUDA Paste Extruder
-//#define BARICUDA
-
 // Support for BlinkM/CyzRgb
 //#define BLINKM
 
@@ -2346,16 +3260,19 @@
  * luminance values can be set from 0 to 255.
  * For NeoPixel LED an overall brightness parameter is also available.
  *
- * *** CAUTION ***
+ *  === CAUTION ===
  *  LED Strips require a MOSFET Chip between PWM lines and LEDs,
  *  as the Arduino cannot handle the current the LEDs will require.
  *  Failure to follow this precaution can destroy your Arduino!
+ *
  *  NOTE: A separate 5V power supply is required! The NeoPixel LED needs
  *  more current than the Arduino 5V linear regulator can produce.
- * *** CAUTION ***
  *
- * LED Type. Enable only one of the following two options.
+ *  Requires PWM frequency between 50 <> 100Hz (Check HAL or variant)
+ *  Use FAST_PWM_FAN, if possible, to reduce fan noise.
  */
+
+// LED Type. Enable only one of the following two options:
 //#define RGB_LED
 //#define RGBW_LED
 
@@ -2364,33 +3281,41 @@
   //#define RGB_LED_G_PIN 43
   //#define RGB_LED_B_PIN 35
   //#define RGB_LED_W_PIN -1
+  //#define RGB_STARTUP_TEST              // For PWM pins, fade between all colors
+  #if ENABLED(RGB_STARTUP_TEST)
+    #define RGB_STARTUP_TEST_INNER_MS 10  // (ms) Reduce or increase fading speed
+  #endif
 #endif
 
 // Support for Adafruit NeoPixel LED driver
 //#define NEOPIXEL_LED
 #if ENABLED(NEOPIXEL_LED)
-  #define NEOPIXEL_TYPE   NEO_GRBW // NEO_GRBW / NEO_GRB - four/three channel driver type (defined in Adafruit_NeoPixel.h)
-  #define NEOPIXEL_PIN     4       // LED driving pin
-  //#define NEOPIXEL2_TYPE NEOPIXEL_TYPE
-  //#define NEOPIXEL2_PIN    5
-  #define NEOPIXEL_PIXELS 30       // Number of LEDs in the strip. (Longest strip when NEOPIXEL2_SEPARATE is disabled.)
-  #define NEOPIXEL_IS_SEQUENTIAL   // Sequential display for temperature change - LED by LED. Disable to change all LEDs at once.
-  #define NEOPIXEL_BRIGHTNESS 127  // Initial brightness (0-255)
-  //#define NEOPIXEL_STARTUP_TEST  // Cycle through colors at startup
+  #define NEOPIXEL_TYPE          NEO_GRBW // NEO_GRBW, NEO_RGBW, NEO_GRB, NEO_RBG, etc.
+                                          // See https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.h
+  //#define NEOPIXEL_PIN                4 // LED driving pin
+  //#define NEOPIXEL2_TYPE  NEOPIXEL_TYPE
+  //#define NEOPIXEL2_PIN               5
+  #define NEOPIXEL_PIXELS              30 // Number of LEDs in the strip. (Longest strip when NEOPIXEL2_SEPARATE is disabled.)
+  #define NEOPIXEL_IS_SEQUENTIAL          // Sequential display for temperature change - LED by LED. Disable to change all LEDs at once.
+  #define NEOPIXEL_BRIGHTNESS         127 // Initial brightness (0-255)
+  //#define NEOPIXEL_STARTUP_TEST         // Cycle through colors at startup
 
   // Support for second Adafruit NeoPixel LED driver controlled with M150 S1 ...
   //#define NEOPIXEL2_SEPARATE
   #if ENABLED(NEOPIXEL2_SEPARATE)
-    #define NEOPIXEL2_PIXELS      15  // Number of LEDs in the second strip
-    #define NEOPIXEL2_BRIGHTNESS 127  // Initial brightness (0-255)
-    #define NEOPIXEL2_STARTUP_TEST    // Cycle through colors at startup
+    #define NEOPIXEL2_PIXELS           15 // Number of LEDs in the second strip
+    #define NEOPIXEL2_BRIGHTNESS      127 // Initial brightness (0-255)
+    #define NEOPIXEL2_STARTUP_TEST        // Cycle through colors at startup
+    #define NEOPIXEL_M150_DEFAULT      -1 // Default strip for M150 without 'S'. Use -1 to set all by default.
   #else
-    //#define NEOPIXEL2_INSERIES      // Default behavior is NeoPixel 2 in parallel
+    //#define NEOPIXEL2_INSERIES          // Default behavior is NeoPixel 2 in parallel
   #endif
 
-  // Use a single NeoPixel LED for static (background) lighting
-  //#define NEOPIXEL_BKGD_LED_INDEX  0               // Index of the LED to use
-  //#define NEOPIXEL_BKGD_COLOR { 255, 255, 255, 0 } // R, G, B, W
+  // Use some of the NeoPixel LEDs for static (background) lighting
+  //#define NEOPIXEL_BKGD_INDEX_FIRST   0 // Index of the first background LED
+  //#define NEOPIXEL_BKGD_INDEX_LAST    5 // Index of the last background LED
+  //#define NEOPIXEL_BKGD_COLOR { 255, 255, 255, 0 }  // R, G, B, W
+  //#define NEOPIXEL_BKGD_ALWAYS_ON       // Keep the backlight on when other NeoPixels are off
 #endif
 
 /**
@@ -2408,6 +3333,8 @@
   #define PRINTER_EVENT_LEDS
 #endif
 
+// @section servos
+
 /**
  * Number of servos
  *
@@ -2415,9 +3342,9 @@
  * Set this manually if there are extra servos needing manual control.
  * Set to 0 to turn off servo support.
  */
-//#define NUM_SERVOS 3 // Servo index starts with 0 for M280 command
+//#define NUM_SERVOS 3 // Note: Servo index starts with 0 for M280-M282 commands
 
-// (ms) Delay  before the next move will start, to give the servo time to reach its target angle.
+// (ms) Delay before the next move will start, to give the servo time to reach its target angle.
 // 300ms is a good value but you can try less delay.
 // If the servo can't reach the requested position, increase it.
 #define SERVO_DELAY { 300 }
@@ -2427,3 +3354,6 @@
 
 // Edit servo angles with M281 and save to EEPROM with M500
 //#define EDITABLE_SERVO_ANGLES
+
+// Disable servo with M282 to reduce power consumption, noise, and heat when not in use
+//#define SERVO_DETACH_GCODE
diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h
index 0f72ed5eb3..c4401143ef 100644
--- a/Marlin/Configuration_adv.h
+++ b/Marlin/Configuration_adv.h
@@ -30,7 +30,25 @@
  *
  * Basic settings can be found in Configuration.h
  */
-#define CONFIGURATION_ADV_H_VERSION 020007
+#define CONFIGURATION_ADV_H_VERSION 02010200
+
+// @section develop
+
+/**
+ * Configuration Export
+ *
+ * Export the configuration as part of the build. (See signature.py)
+ * Output files are saved with the build (e.g., .pio/build/mega2560).
+ *
+ * See `build_all_examples --ini` as an example of config.ini archiving.
+ *
+ *  1 = marlin_config.json - Dictionary containing the configuration.
+ *      This file is also generated for CONFIGURATION_EMBEDDING.
+ *  2 = config.ini - File format for PlatformIO preprocessing.
+ *  3 = schema.json - The entire configuration schema. (13 = pattern groups)
+ *  4 = schema.yml - The entire configuration schema.
+ */
+//#define CONFIG_EXPORT 2 // :[1:'JSON', 2:'config.ini', 3:'schema.json', 4:'schema.yml']
 
 //===========================================================================
 //============================= Thermal Settings ============================
@@ -54,69 +72,126 @@
 // Custom Thermistor 1000 parameters
 //
 #if TEMP_SENSOR_0 == 1000
-  #define HOTEND0_PULLUP_RESISTOR_OHMS 4700    // Pullup resistor
-  #define HOTEND0_RESISTANCE_25C_OHMS  100000  // Resistance at 25C
-  #define HOTEND0_BETA                 3950    // Beta value
+  #define HOTEND0_PULLUP_RESISTOR_OHMS    4700 // Pullup resistor
+  #define HOTEND0_RESISTANCE_25C_OHMS   100000 // Resistance at 25C
+  #define HOTEND0_BETA                    3950 // Beta value
+  #define HOTEND0_SH_C_COEFF                 0 // Steinhart-Hart C coefficient
 #endif
 
 #if TEMP_SENSOR_1 == 1000
-  #define HOTEND1_PULLUP_RESISTOR_OHMS 4700    // Pullup resistor
-  #define HOTEND1_RESISTANCE_25C_OHMS  100000  // Resistance at 25C
-  #define HOTEND1_BETA                 3950    // Beta value
+  #define HOTEND1_PULLUP_RESISTOR_OHMS    4700 // Pullup resistor
+  #define HOTEND1_RESISTANCE_25C_OHMS   100000 // Resistance at 25C
+  #define HOTEND1_BETA                    3950 // Beta value
+  #define HOTEND1_SH_C_COEFF                 0 // Steinhart-Hart C coefficient
 #endif
 
 #if TEMP_SENSOR_2 == 1000
-  #define HOTEND2_PULLUP_RESISTOR_OHMS 4700    // Pullup resistor
-  #define HOTEND2_RESISTANCE_25C_OHMS  100000  // Resistance at 25C
-  #define HOTEND2_BETA                 3950    // Beta value
+  #define HOTEND2_PULLUP_RESISTOR_OHMS    4700 // Pullup resistor
+  #define HOTEND2_RESISTANCE_25C_OHMS   100000 // Resistance at 25C
+  #define HOTEND2_BETA                    3950 // Beta value
+  #define HOTEND2_SH_C_COEFF                 0 // Steinhart-Hart C coefficient
 #endif
 
 #if TEMP_SENSOR_3 == 1000
-  #define HOTEND3_PULLUP_RESISTOR_OHMS 4700    // Pullup resistor
-  #define HOTEND3_RESISTANCE_25C_OHMS  100000  // Resistance at 25C
-  #define HOTEND3_BETA                 3950    // Beta value
+  #define HOTEND3_PULLUP_RESISTOR_OHMS    4700 // Pullup resistor
+  #define HOTEND3_RESISTANCE_25C_OHMS   100000 // Resistance at 25C
+  #define HOTEND3_BETA                    3950 // Beta value
+  #define HOTEND3_SH_C_COEFF                 0 // Steinhart-Hart C coefficient
 #endif
 
 #if TEMP_SENSOR_4 == 1000
-  #define HOTEND4_PULLUP_RESISTOR_OHMS 4700    // Pullup resistor
-  #define HOTEND4_RESISTANCE_25C_OHMS  100000  // Resistance at 25C
-  #define HOTEND4_BETA                 3950    // Beta value
+  #define HOTEND4_PULLUP_RESISTOR_OHMS    4700 // Pullup resistor
+  #define HOTEND4_RESISTANCE_25C_OHMS   100000 // Resistance at 25C
+  #define HOTEND4_BETA                    3950 // Beta value
+  #define HOTEND4_SH_C_COEFF                 0 // Steinhart-Hart C coefficient
 #endif
 
 #if TEMP_SENSOR_5 == 1000
-  #define HOTEND5_PULLUP_RESISTOR_OHMS 4700    // Pullup resistor
-  #define HOTEND5_RESISTANCE_25C_OHMS  100000  // Resistance at 25C
-  #define HOTEND5_BETA                 3950    // Beta value
+  #define HOTEND5_PULLUP_RESISTOR_OHMS    4700 // Pullup resistor
+  #define HOTEND5_RESISTANCE_25C_OHMS   100000 // Resistance at 25C
+  #define HOTEND5_BETA                    3950 // Beta value
+  #define HOTEND5_SH_C_COEFF                 0 // Steinhart-Hart C coefficient
 #endif
 
 #if TEMP_SENSOR_6 == 1000
-  #define HOTEND6_PULLUP_RESISTOR_OHMS 4700    // Pullup resistor
-  #define HOTEND6_RESISTANCE_25C_OHMS  100000  // Resistance at 25C
-  #define HOTEND6_BETA                 3950    // Beta value
+  #define HOTEND6_PULLUP_RESISTOR_OHMS    4700 // Pullup resistor
+  #define HOTEND6_RESISTANCE_25C_OHMS   100000 // Resistance at 25C
+  #define HOTEND6_BETA                    3950 // Beta value
+  #define HOTEND6_SH_C_COEFF                 0 // Steinhart-Hart C coefficient
 #endif
 
 #if TEMP_SENSOR_7 == 1000
-  #define HOTEND7_PULLUP_RESISTOR_OHMS 4700    // Pullup resistor
-  #define HOTEND7_RESISTANCE_25C_OHMS  100000  // Resistance at 25C
-  #define HOTEND7_BETA                 3950    // Beta value
+  #define HOTEND7_PULLUP_RESISTOR_OHMS    4700 // Pullup resistor
+  #define HOTEND7_RESISTANCE_25C_OHMS   100000 // Resistance at 25C
+  #define HOTEND7_BETA                    3950 // Beta value
+  #define HOTEND7_SH_C_COEFF                 0 // Steinhart-Hart C coefficient
 #endif
 
 #if TEMP_SENSOR_BED == 1000
-  #define BED_PULLUP_RESISTOR_OHMS     4700    // Pullup resistor
-  #define BED_RESISTANCE_25C_OHMS      100000  // Resistance at 25C
-  #define BED_BETA                     3950    // Beta value
+  #define BED_PULLUP_RESISTOR_OHMS        4700 // Pullup resistor
+  #define BED_RESISTANCE_25C_OHMS       100000 // Resistance at 25C
+  #define BED_BETA                        3950 // Beta value
+  #define BED_SH_C_COEFF                     0 // Steinhart-Hart C coefficient
 #endif
 
 #if TEMP_SENSOR_CHAMBER == 1000
-  #define CHAMBER_PULLUP_RESISTOR_OHMS 4700    // Pullup resistor
-  #define CHAMBER_RESISTANCE_25C_OHMS  100000  // Resistance at 25C
-  #define CHAMBER_BETA                 3950    // Beta value
+  #define CHAMBER_PULLUP_RESISTOR_OHMS    4700 // Pullup resistor
+  #define CHAMBER_RESISTANCE_25C_OHMS   100000 // Resistance at 25C
+  #define CHAMBER_BETA                    3950 // Beta value
+  #define CHAMBER_SH_C_COEFF                 0 // Steinhart-Hart C coefficient
 #endif
 
-//
-// Hephestos 2 24V heated bed upgrade kit.
-// https://store.bq.com/en/heated-bed-kit-hephestos2
-//
+#if TEMP_SENSOR_COOLER == 1000
+  #define COOLER_PULLUP_RESISTOR_OHMS     4700 // Pullup resistor
+  #define COOLER_RESISTANCE_25C_OHMS    100000 // Resistance at 25C
+  #define COOLER_BETA                     3950 // Beta value
+  #define COOLER_SH_C_COEFF                  0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_PROBE == 1000
+  #define PROBE_PULLUP_RESISTOR_OHMS      4700 // Pullup resistor
+  #define PROBE_RESISTANCE_25C_OHMS     100000 // Resistance at 25C
+  #define PROBE_BETA                      3950 // Beta value
+  #define PROBE_SH_C_COEFF                   0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_BOARD == 1000
+  #define BOARD_PULLUP_RESISTOR_OHMS      4700 // Pullup resistor
+  #define BOARD_RESISTANCE_25C_OHMS     100000 // Resistance at 25C
+  #define BOARD_BETA                      3950 // Beta value
+  #define BOARD_SH_C_COEFF                   0 // Steinhart-Hart C coefficient
+#endif
+
+#if TEMP_SENSOR_REDUNDANT == 1000
+  #define REDUNDANT_PULLUP_RESISTOR_OHMS  4700 // Pullup resistor
+  #define REDUNDANT_RESISTANCE_25C_OHMS 100000 // Resistance at 25C
+  #define REDUNDANT_BETA                  3950 // Beta value
+  #define REDUNDANT_SH_C_COEFF               0 // Steinhart-Hart C coefficient
+#endif
+
+/**
+ * Thermocouple Options — for MAX6675 (-2), MAX31855 (-3), and MAX31865 (-5).
+ */
+//#define TEMP_SENSOR_FORCE_HW_SPI                // Ignore SCK/MOSI/MISO pins; use CS and the default SPI bus.
+//#define MAX31865_SENSOR_WIRES_0 2               // (2-4) Number of wires for the probe connected to a MAX31865 board.
+//#define MAX31865_SENSOR_WIRES_1 2
+//#define MAX31865_SENSOR_WIRES_2 2
+
+//#define MAX31865_50HZ_FILTER                    // Use a 50Hz filter instead of the default 60Hz.
+//#define MAX31865_USE_READ_ERROR_DETECTION       // Treat value spikes (20°C delta in under 1s) as read errors.
+
+//#define MAX31865_USE_AUTO_MODE                  // Read faster and more often than 1-shot; bias voltage always on; slight effect on RTD temperature.
+//#define MAX31865_MIN_SAMPLING_TIME_MSEC     100 // (ms) 1-shot: minimum read interval. Reduces bias voltage effects by leaving sensor unpowered for longer intervals.
+//#define MAX31865_IGNORE_INITIAL_FAULTY_READS 10 // Ignore some read faults (keeping the temperature reading) to work around a possible issue (#23439).
+
+//#define MAX31865_WIRE_OHMS_0              0.95f // For 2-wire, set the wire resistances for more accurate readings.
+//#define MAX31865_WIRE_OHMS_1              0.0f
+//#define MAX31865_WIRE_OHMS_2              0.0f
+
+/**
+ * Hephestos 2 24V heated bed upgrade kit.
+ * https://store.bq.com/en/heated-bed-kit-hephestos2
+ */
 //#define HEPHESTOS2_HEATED_BED_KIT
 #if ENABLED(HEPHESTOS2_HEATED_BED_KIT)
   #undef TEMP_SENSOR_BED
@@ -137,17 +212,22 @@
 //
 // Heated Chamber options
 //
+#if DISABLED(PIDTEMPCHAMBER)
+  #define CHAMBER_CHECK_INTERVAL 5000   // (ms) Interval between checks in bang-bang control
+  #if ENABLED(CHAMBER_LIMIT_SWITCHING)
+    #define CHAMBER_HYSTERESIS 2        // (°C) Only set the relevant heater state when ABS(T-target) > CHAMBER_HYSTERESIS
+  #endif
+#endif
+
 #if TEMP_SENSOR_CHAMBER
-  #define CHAMBER_MINTEMP             5
-  #define CHAMBER_MAXTEMP            60
-  #define TEMP_CHAMBER_HYSTERESIS     1   // (°C) Temperature proximity considered "close enough" to the target
-  //#define CHAMBER_LIMIT_SWITCHING
-  //#define HEATER_CHAMBER_PIN       44   // Chamber heater on/off pin
+  //#define HEATER_CHAMBER_PIN      P2_04   // Required heater on/off pin (example: SKR 1.4 Turbo HE1 plug)
   //#define HEATER_CHAMBER_INVERTING false
+  //#define FAN1_PIN                   -1   // Remove the fan signal on pin P2_04 (example: SKR 1.4 Turbo HE1 plug)
 
   //#define CHAMBER_FAN               // Enable a fan on the chamber
   #if ENABLED(CHAMBER_FAN)
-    #define CHAMBER_FAN_MODE 2        // Fan control mode: 0=Static; 1=Linear increase when temp is higher than target; 2=V-shaped curve.
+    //#define CHAMBER_FAN_INDEX   2   // Index of a fan to repurpose as the chamber fan. (Default: first unused fan)
+    #define CHAMBER_FAN_MODE      2   // Fan control mode: 0=Static; 1=Linear increase when temp is higher than target; 2=V-shaped curve; 3=similar to 1 but fan is always on.
     #if CHAMBER_FAN_MODE == 0
       #define CHAMBER_FAN_BASE  255   // Chamber fan PWM (0-255)
     #elif CHAMBER_FAN_MODE == 1
@@ -156,6 +236,9 @@
     #elif CHAMBER_FAN_MODE == 2
       #define CHAMBER_FAN_BASE  128   // Minimum chamber fan PWM (0-255)
       #define CHAMBER_FAN_FACTOR 25   // PWM increase per °C difference from target
+    #elif CHAMBER_FAN_MODE == 3
+      #define CHAMBER_FAN_BASE  128   // Base chamber fan PWM (0-255)
+      #define CHAMBER_FAN_FACTOR 25   // PWM increase per °C above target
     #endif
   #endif
 
@@ -163,12 +246,43 @@
   #if ENABLED(CHAMBER_VENT)
     #define CHAMBER_VENT_SERVO_NR  1  // Index of the vent servo
     #define HIGH_EXCESS_HEAT_LIMIT 5  // How much above target temp to consider there is excess heat in the chamber
-    #define LOW_EXCESS_HEAT_LIMIT 3
+    #define LOW_EXCESS_HEAT_LIMIT  3
     #define MIN_COOLING_SLOPE_TIME_CHAMBER_VENT 20
     #define MIN_COOLING_SLOPE_DEG_CHAMBER_VENT 1.5
   #endif
 #endif
 
+//
+// Laser Cooler options
+//
+#if TEMP_SENSOR_COOLER
+  #define COOLER_MINTEMP           8  // (°C)
+  #define COOLER_MAXTEMP          26  // (°C)
+  #define COOLER_DEFAULT_TEMP     16  // (°C)
+  #define TEMP_COOLER_HYSTERESIS   1  // (°C) Temperature proximity considered "close enough" to the target
+  #define COOLER_PIN               8  // Laser cooler on/off pin used to control power to the cooling element (e.g., TEC, External chiller via relay)
+  #define COOLER_INVERTING     false
+  #define TEMP_COOLER_PIN         15  // Laser/Cooler temperature sensor pin. ADC is required.
+  #define COOLER_FAN                  // Enable a fan on the cooler, Fan# 0,1,2,3 etc.
+  #define COOLER_FAN_INDEX         0  // FAN number 0, 1, 2 etc. e.g.
+  #if ENABLED(COOLER_FAN)
+    #define COOLER_FAN_BASE      100  // Base Cooler fan PWM (0-255); turns on when Cooler temperature is above the target
+    #define COOLER_FAN_FACTOR     25  // PWM increase per °C above target
+  #endif
+#endif
+
+//
+// Motherboard Sensor options
+//
+#if TEMP_SENSOR_BOARD
+  #define THERMAL_PROTECTION_BOARD   // Halt the printer if the board sensor leaves the temp range below.
+  #define BOARD_MINTEMP           8  // (°C)
+  #define BOARD_MAXTEMP          70  // (°C)
+  #ifndef TEMP_BOARD_PIN
+    //#define TEMP_BOARD_PIN -1      // Board temp sensor pin, if not set in pins file.
+  #endif
+#endif
+
 /**
  * Thermal Protection provides additional protection to your printer from damage
  * and fire. Marlin always includes safe min and max temperature ranges which
@@ -206,7 +320,7 @@
    * and/or decrease WATCH_TEMP_INCREASE. WATCH_TEMP_INCREASE should not be set
    * below 2.
    */
-  #define WATCH_TEMP_PERIOD 20                // Seconds
+  #define WATCH_TEMP_PERIOD  40               // Seconds
   #define WATCH_TEMP_INCREASE 2               // Degrees Celsius
 #endif
 
@@ -238,6 +352,28 @@
   #define WATCH_CHAMBER_TEMP_INCREASE           2 // Degrees Celsius
 #endif
 
+/**
+ * Thermal Protection parameters for the laser cooler.
+ */
+#if ENABLED(THERMAL_PROTECTION_COOLER)
+  #define THERMAL_PROTECTION_COOLER_PERIOD     10 // Seconds
+  #define THERMAL_PROTECTION_COOLER_HYSTERESIS  3 // Degrees Celsius
+
+  /**
+   * Laser cooling watch settings (M143/M193).
+   */
+  #define WATCH_COOLER_TEMP_PERIOD             60 // Seconds
+  #define WATCH_COOLER_TEMP_INCREASE            3 // Degrees Celsius
+#endif
+
+#if ANY(THERMAL_PROTECTION_HOTENDS, THERMAL_PROTECTION_BED, THERMAL_PROTECTION_CHAMBER, THERMAL_PROTECTION_COOLER)
+  /**
+   * Thermal Protection Variance Monitor - EXPERIMENTAL.
+   * Kill the machine on a stuck temperature sensor. Disable if you get false positives.
+   */
+  //#define THERMAL_PROTECTION_VARIANCE_MONITOR   // Detect a sensor malfunction preventing temperature updates
+#endif
+
 #if ENABLED(PIDTEMP)
   // Add an experimental additional term to the heater power, proportional to the extrusion speed.
   // A well-chosen Kc value should add just enough power to melt the increased material volume.
@@ -284,8 +420,8 @@
       // DEFAULT_Kf and PID_FAN_SCALING_LIN_FACTOR are calculated accordingly.
 
       #define PID_FAN_SCALING_AT_FULL_SPEED 13.0        //=PID_FAN_SCALING_LIN_FACTOR*255+DEFAULT_Kf
-      #define PID_FAN_SCALING_AT_MIN_SPEED 6.0          //=PID_FAN_SCALING_LIN_FACTOR*PID_FAN_SCALING_MIN_SPEED+DEFAULT_Kf
-      #define PID_FAN_SCALING_MIN_SPEED 10.0            // Minimum fan speed at which to enable PID_FAN_SCALING
+      #define PID_FAN_SCALING_AT_MIN_SPEED   6.0        //=PID_FAN_SCALING_LIN_FACTOR*PID_FAN_SCALING_MIN_SPEED+DEFAULT_Kf
+      #define PID_FAN_SCALING_MIN_SPEED     10.0        // Minimum fan speed at which to enable PID_FAN_SCALING
 
       #define DEFAULT_Kf (255.0*PID_FAN_SCALING_AT_MIN_SPEED-PID_FAN_SCALING_AT_FULL_SPEED*PID_FAN_SCALING_MIN_SPEED)/(255.0-PID_FAN_SCALING_MIN_SPEED)
       #define PID_FAN_SCALING_LIN_FACTOR (PID_FAN_SCALING_AT_FULL_SPEED-DEFAULT_Kf)/255.0
@@ -313,7 +449,7 @@
  */
 #define AUTOTEMP
 #if ENABLED(AUTOTEMP)
-  #define AUTOTEMP_OLDWEIGHT    0.98
+  #define AUTOTEMP_OLDWEIGHT    0.98  // Factor used to weight previous readings (0.0 < value < 1.0)
   // Turn on AUTOTEMP on M104/M109 by default using proportions set here
   //#define AUTOTEMP_PROPORTIONAL
   #if ENABLED(AUTOTEMP_PROPORTIONAL)
@@ -331,7 +467,7 @@
  * High Temperature Thermistor Support
  *
  * Thermistors able to support high temperature tend to have a hard time getting
- * good readings at room and lower temperatures. This means HEATER_X_RAW_LO_TEMP
+ * good readings at room and lower temperatures. This means TEMP_SENSOR_X_RAW_LO_TEMP
  * will probably be caught when the heating element first turns on during the
  * preheating process, which will trigger a min_temp_error as a safety measure
  * and force stop everything.
@@ -347,18 +483,22 @@
 // before a min_temp_error is triggered. (Shouldn't be more than 10.)
 //#define MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWED 0
 
-// The number of milliseconds a hotend will preheat before starting to check
-// the temperature. This value should NOT be set to the time it takes the
-// hot end to reach the target temperature, but the time it takes to reach
-// the minimum temperature your thermistor can read. The lower the better/safer.
-// This shouldn't need to be more than 30 seconds (30000)
+/**
+ * The number of milliseconds a hotend will preheat before starting to check
+ * the temperature. This value should NOT be set to the time it takes the
+ * hot end to reach the target temperature, but the time it takes to reach
+ * the minimum temperature your thermistor can read. The lower the better/safer.
+ * This shouldn't need to be more than 30 seconds (30000)
+ */
 //#define MILLISECONDS_PREHEAT_TIME 0
 
 // @section extruder
 
-// Extruder runout prevention.
-// If the machine is idle and the temperature over MINTEMP
-// then extrude some filament every couple of SECONDS.
+/**
+ * Extruder runout prevention.
+ * If the machine is idle and the temperature over MINTEMP
+ * then extrude some filament every couple of SECONDS.
+ */
 //#define EXTRUDER_RUNOUT_PREVENT
 #if ENABLED(EXTRUDER_RUNOUT_PREVENT)
   #define EXTRUDER_RUNOUT_MINTEMP 190
@@ -397,23 +537,32 @@
  */
 //#define USE_CONTROLLER_FAN
 #if ENABLED(USE_CONTROLLER_FAN)
-  //#define CONTROLLER_FAN_PIN -1        // Set a custom pin for the controller fan
-  //#define CONTROLLER_FAN_USE_Z_ONLY    // With this option only the Z axis is considered
-  //#define CONTROLLER_FAN_IGNORE_Z      // Ignore Z stepper. Useful when stepper timeout is disabled.
-  #define CONTROLLERFAN_SPEED_MIN      0 // (0-255) Minimum speed. (If set below this value the fan is turned off.)
-  #define CONTROLLERFAN_SPEED_ACTIVE 255 // (0-255) Active speed, used when any motor is enabled
-  #define CONTROLLERFAN_SPEED_IDLE     0 // (0-255) Idle speed, used when motors are disabled
-  #define CONTROLLERFAN_IDLE_TIME     60 // (seconds) Extra time to keep the fan running after disabling motors
-  //#define CONTROLLER_FAN_EDITABLE      // Enable M710 configurable settings
+  //#define CONTROLLER_FAN_PIN -1           // Set a custom pin for the controller fan
+  //#define CONTROLLER_FAN2_PIN -1          // Set a custom pin for second controller fan
+  //#define CONTROLLER_FAN_USE_Z_ONLY       // With this option only the Z axis is considered
+  //#define CONTROLLER_FAN_IGNORE_Z         // Ignore Z stepper. Useful when stepper timeout is disabled.
+  #define CONTROLLERFAN_SPEED_MIN         0 // (0-255) Minimum speed. (If set below this value the fan is turned off.)
+  #define CONTROLLERFAN_SPEED_ACTIVE    255 // (0-255) Active speed, used when any motor is enabled
+  #define CONTROLLERFAN_SPEED_IDLE        0 // (0-255) Idle speed, used when motors are disabled
+  #define CONTROLLERFAN_IDLE_TIME        60 // (seconds) Extra time to keep the fan running after disabling motors
+
+  // Use TEMP_SENSOR_BOARD as a trigger for enabling the controller fan
+  //#define CONTROLLER_FAN_MIN_BOARD_TEMP 40  // (°C) Turn on the fan if the board reaches this temperature
+
+  //#define CONTROLLER_FAN_EDITABLE         // Enable M710 configurable settings
   #if ENABLED(CONTROLLER_FAN_EDITABLE)
-    #define CONTROLLER_FAN_MENU          // Enable the Controller Fan submenu
+    #define CONTROLLER_FAN_MENU             // Enable the Controller Fan submenu
   #endif
 #endif
 
-// When first starting the main fan, run it at full speed for the
-// given number of milliseconds.  This gets the fan spinning reliably
-// before setting a PWM value. (Does not work with software PWM for fan on Sanguinololu)
-//#define FAN_KICKSTART_TIME 100
+/**
+ * Fan Kickstart
+ * When part cooling or controller fans first start, run at a speed that
+ * gets it spinning reliably for a short time before setting the requested speed.
+ * (Does not work on Sanguinololu with FAN_SOFT_PWM.)
+ */
+//#define FAN_KICKSTART_TIME  100  // (ms)
+//#define FAN_KICKSTART_POWER 180  // 64-255
 
 // Some coolers may require a non-zero "off" state.
 //#define FAN_OFF_PWM  1
@@ -434,32 +583,48 @@
 //#define FAN_MAX_PWM 128
 
 /**
- * FAST PWM FAN Settings
+ * Fan Fast PWM
  *
- * Use to change the FAST FAN PWM frequency (if enabled in Configuration.h)
- * Combinations of PWM Modes, prescale values and TOP resolutions are used internally to produce a
- * frequency as close as possible to the desired frequency.
+ * Combinations of PWM Modes, prescale values and TOP resolutions are used internally
+ * to produce a frequency as close as possible to the desired frequency.
  *
- * FAST_PWM_FAN_FREQUENCY [undefined by default]
+ * FAST_PWM_FAN_FREQUENCY
  *   Set this to your desired frequency.
- *   If left undefined this defaults to F = F_CPU/(2*255*1)
- *   i.e., F = 31.4kHz on 16MHz microcontrollers or F = 39.2kHz on 20MHz microcontrollers.
- *   These defaults are the same as with the old FAST_PWM_FAN implementation - no migration is required
+ *   For AVR, if left undefined this defaults to F = F_CPU/(2*255*1)
+ *            i.e., F = 31.4kHz on 16MHz microcontrollers or F = 39.2kHz on 20MHz microcontrollers.
+ *   For non AVR, if left undefined this defaults to F = 1Khz.
+ *   This F value is only to protect the hardware from an absence of configuration
+ *   and not to complete it when users are not aware that the frequency must be specifically set to support the target board.
+ *
  *   NOTE: Setting very low frequencies (< 10 Hz) may result in unexpected timer behavior.
+ *         Setting very high frequencies can damage your hardware.
  *
  * USE_OCR2A_AS_TOP [undefined by default]
  *   Boards that use TIMER2 for PWM have limitations resulting in only a few possible frequencies on TIMER2:
- *   16MHz MCUs: [62.5KHz, 31.4KHz (default), 7.8KHz, 3.92KHz, 1.95KHz, 977Hz, 488Hz, 244Hz, 60Hz, 122Hz, 30Hz]
- *   20MHz MCUs: [78.1KHz, 39.2KHz (default), 9.77KHz, 4.9KHz, 2.44KHz, 1.22KHz, 610Hz, 305Hz, 153Hz, 76Hz, 38Hz]
+ *   16MHz MCUs: [62.5kHz, 31.4kHz (default), 7.8kHz, 3.92kHz, 1.95kHz, 977Hz, 488Hz, 244Hz, 60Hz, 122Hz, 30Hz]
+ *   20MHz MCUs: [78.1kHz, 39.2kHz (default), 9.77kHz, 4.9kHz, 2.44kHz, 1.22kHz, 610Hz, 305Hz, 153Hz, 76Hz, 38Hz]
  *   A greater range can be achieved by enabling USE_OCR2A_AS_TOP. But note that this option blocks the use of
  *   PWM on pin OC2A. Only use this option if you don't need PWM on 0C2A. (Check your schematic.)
  *   USE_OCR2A_AS_TOP sacrifices duty cycle control resolution to achieve this broader range of frequencies.
  */
+//#define FAST_PWM_FAN    // Increase the fan PWM frequency. Removes the PWM noise but increases heating in the FET/Arduino
 #if ENABLED(FAST_PWM_FAN)
-  //#define FAST_PWM_FAN_FREQUENCY 31400
+  //#define FAST_PWM_FAN_FREQUENCY 31400  // Define here to override the defaults below
   //#define USE_OCR2A_AS_TOP
+  #ifndef FAST_PWM_FAN_FREQUENCY
+    #ifdef __AVR__
+      #define FAST_PWM_FAN_FREQUENCY ((F_CPU) / (2 * 255 * 1))
+    #else
+      #define FAST_PWM_FAN_FREQUENCY 1000U
+    #endif
+  #endif
 #endif
 
+/**
+ * Use one of the PWM fans as a redundant part-cooling fan
+ */
+//#define REDUNDANT_PART_COOLING_FAN 2  // Index of the fan to sync with FAN 0.
+
 // @section extruder
 
 /**
@@ -483,11 +648,48 @@
 #define E6_AUTO_FAN_PIN -1
 #define E7_AUTO_FAN_PIN -1
 #define CHAMBER_AUTO_FAN_PIN -1
+#define COOLER_AUTO_FAN_PIN -1
 
 #define EXTRUDER_AUTO_FAN_TEMPERATURE 50
 #define EXTRUDER_AUTO_FAN_SPEED 255   // 255 == full speed
 #define CHAMBER_AUTO_FAN_TEMPERATURE 30
 #define CHAMBER_AUTO_FAN_SPEED 255
+#define COOLER_AUTO_FAN_TEMPERATURE 18
+#define COOLER_AUTO_FAN_SPEED 255
+
+/**
+ * Hotend Cooling Fans tachometers
+ *
+ * Define one or more tachometer pins to enable fan speed
+ * monitoring, and reporting of fan speeds with M123.
+ *
+ * NOTE: Only works with fans up to 7000 RPM.
+ */
+//#define FOURWIRES_FANS      // Needed with AUTO_FAN when 4-wire PWM fans are installed
+//#define E0_FAN_TACHO_PIN -1
+//#define E0_FAN_TACHO_PULLUP
+//#define E0_FAN_TACHO_PULLDOWN
+//#define E1_FAN_TACHO_PIN -1
+//#define E1_FAN_TACHO_PULLUP
+//#define E1_FAN_TACHO_PULLDOWN
+//#define E2_FAN_TACHO_PIN -1
+//#define E2_FAN_TACHO_PULLUP
+//#define E2_FAN_TACHO_PULLDOWN
+//#define E3_FAN_TACHO_PIN -1
+//#define E3_FAN_TACHO_PULLUP
+//#define E3_FAN_TACHO_PULLDOWN
+//#define E4_FAN_TACHO_PIN -1
+//#define E4_FAN_TACHO_PULLUP
+//#define E4_FAN_TACHO_PULLDOWN
+//#define E5_FAN_TACHO_PIN -1
+//#define E5_FAN_TACHO_PULLUP
+//#define E5_FAN_TACHO_PULLDOWN
+//#define E6_FAN_TACHO_PIN -1
+//#define E6_FAN_TACHO_PULLUP
+//#define E6_FAN_TACHO_PULLDOWN
+//#define E7_FAN_TACHO_PIN -1
+//#define E7_FAN_TACHO_PULLUP
+//#define E7_FAN_TACHO_PULLDOWN
 
 /**
  * Part-Cooling Fan Multiplexer
@@ -509,12 +711,17 @@
   #define INVERT_CASE_LIGHT false             // Set true if Case Light is ON when pin is LOW
   #define CASE_LIGHT_DEFAULT_ON true          // Set default power-up state on
   #define CASE_LIGHT_DEFAULT_BRIGHTNESS 105   // Set default power-up brightness (0-255, requires PWM pin)
-  //#define CASE_LIGHT_MAX_PWM 128            // Limit pwm
-  //#define CASE_LIGHT_MENU                   // Add Case Light options to the LCD menu
   //#define CASE_LIGHT_NO_BRIGHTNESS          // Disable brightness control. Enable for non-PWM lighting.
-  //#define CASE_LIGHT_USE_NEOPIXEL           // Use NeoPixel LED as case light, requires NEOPIXEL_LED.
-  #if ENABLED(CASE_LIGHT_USE_NEOPIXEL)
-    #define CASE_LIGHT_NEOPIXEL_COLOR { 255, 255, 255, 255 } // { Red, Green, Blue, White }
+  //#define CASE_LIGHT_MAX_PWM 128            // Limit PWM duty cycle (0-255)
+  //#define CASE_LIGHT_MENU                   // Add Case Light options to the LCD menu
+  #if ENABLED(NEOPIXEL_LED)
+    //#define CASE_LIGHT_USE_NEOPIXEL         // Use NeoPixel LED as case light
+  #endif
+  #if EITHER(RGB_LED, RGBW_LED)
+    //#define CASE_LIGHT_USE_RGB_LED          // Use RGB / RGBW LED as case light
+  #endif
+  #if EITHER(CASE_LIGHT_USE_NEOPIXEL, CASE_LIGHT_USE_RGB_LED)
+    #define CASE_LIGHT_DEFAULT_COLOR { 255, 255, 255, 255 } // { Red, Green, Blue, White }
   #endif
 #endif
 
@@ -535,62 +742,6 @@
   //#define CLOSED_LOOP_MOVE_COMPLETE_PIN -1
 #endif
 
-/**
- * Dual Steppers / Dual Endstops
- *
- * This section will allow you to use extra E drivers to drive a second motor for X, Y, or Z axes.
- *
- * For example, set X_DUAL_STEPPER_DRIVERS setting to use a second motor. If the motors need to
- * spin in opposite directions set INVERT_X2_VS_X_DIR. If the second motor needs its own endstop
- * set X_DUAL_ENDSTOPS. This can adjust for "racking." Use X2_USE_ENDSTOP to set the endstop plug
- * that should be used for the second endstop. Extra endstops will appear in the output of 'M119'.
- *
- * Use X_DUAL_ENDSTOP_ADJUSTMENT to adjust for mechanical imperfection. After homing both motors
- * this offset is applied to the X2 motor. To find the offset home the X axis, and measure the error
- * in X2. Dual endstop offsets can be set at runtime with 'M666 X Y Z'.
- */
-
-//#define X_DUAL_STEPPER_DRIVERS
-#if ENABLED(X_DUAL_STEPPER_DRIVERS)
-  #define INVERT_X2_VS_X_DIR true   // Set 'true' if X motors should rotate in opposite directions
-  //#define X_DUAL_ENDSTOPS
-  #if ENABLED(X_DUAL_ENDSTOPS)
-    #define X2_USE_ENDSTOP _XMAX_
-    #define X2_ENDSTOP_ADJUSTMENT  0
-  #endif
-#endif
-
-//#define Y_DUAL_STEPPER_DRIVERS
-#if ENABLED(Y_DUAL_STEPPER_DRIVERS)
-  #define INVERT_Y2_VS_Y_DIR true   // Set 'true' if Y motors should rotate in opposite directions
-  //#define Y_DUAL_ENDSTOPS
-  #if ENABLED(Y_DUAL_ENDSTOPS)
-    #define Y2_USE_ENDSTOP _YMAX_
-    #define Y2_ENDSTOP_ADJUSTMENT  0
-  #endif
-#endif
-
-//
-// For Z set the number of stepper drivers
-//
-#define NUM_Z_STEPPER_DRIVERS 2   // (1-4) Z options change based on how many
-
-#if NUM_Z_STEPPER_DRIVERS > 1
-  //#define Z_MULTI_ENDSTOPS
-  #if ENABLED(Z_MULTI_ENDSTOPS)
-    #define Z2_USE_ENDSTOP          _XMAX_
-    #define Z2_ENDSTOP_ADJUSTMENT   0
-    #if NUM_Z_STEPPER_DRIVERS >= 3
-      #define Z3_USE_ENDSTOP        _YMAX_
-      #define Z3_ENDSTOP_ADJUSTMENT 0
-    #endif
-    #if NUM_Z_STEPPER_DRIVERS >= 4
-      #define Z4_USE_ENDSTOP        _ZMAX_
-      #define Z4_ENDSTOP_ADJUSTMENT 0
-    #endif
-  #endif
-#endif
-
 /**
  * Dual X Carriage
  *
@@ -621,22 +772,95 @@
  */
 //#define DUAL_X_CARRIAGE
 #if ENABLED(DUAL_X_CARRIAGE)
-  #define X1_MIN_POS X_MIN_POS   // Set to X_MIN_POS
-  #define X1_MAX_POS X_BED_SIZE  // Set a maximum so the first X-carriage can't hit the parked second X-carriage
-  #define X2_MIN_POS    80       // Set a minimum to ensure the  second X-carriage can't hit the parked first X-carriage
-  #define X2_MAX_POS   353       // Set this to the distance between toolheads when both heads are homed
-  #define X2_HOME_DIR    1       // Set to 1. The second X-carriage always homes to the maximum endstop position
-  #define X2_HOME_POS X2_MAX_POS // Default X2 home position. Set to X2_MAX_POS.
-                      // However: In this mode the HOTEND_OFFSET_X value for the second extruder provides a software
-                      // override for X2_HOME_POS. This also allow recalibration of the distance between the two endstops
-                      // without modifying the firmware (through the "M218 T1 X???" command).
-                      // Remember: you should set the second extruder x-offset to 0 in your slicer.
+  #define X1_MIN_POS X_MIN_POS    // Set to X_MIN_POS
+  #define X1_MAX_POS X_BED_SIZE   // A max coordinate so the X1 carriage can't hit the parked X2 carriage
+  #define X2_MIN_POS    80        // A min coordinate so the X2 carriage can't hit the parked X1 carriage
+  #define X2_MAX_POS   353        // The max position of the X2 carriage, typically also the home position
+  #define X2_HOME_DIR    1        // Set to 1. The X2 carriage always homes to the max endstop position
+  #define X2_HOME_POS X2_MAX_POS  // Default X2 home position. Set to X2_MAX_POS.
+                                  // NOTE: For Dual X Carriage use M218 T1 Xn to override the X2_HOME_POS.
+                                  // This allows recalibration of endstops distance without a rebuild.
+                                  // Remember to set the second extruder's X-offset to 0 in your slicer.
 
-  // This is the default power-up mode which can be later using M605.
+  // This is the default power-up mode which can be changed later using M605 S.
   #define DEFAULT_DUAL_X_CARRIAGE_MODE DXC_AUTO_PARK_MODE
 
   // Default x offset in duplication mode (typically set to half print bed width)
   #define DEFAULT_DUPLICATION_X_OFFSET 100
+
+  // Default action to execute following M605 mode change commands. Typically G28X to apply new mode.
+  //#define EVENT_GCODE_IDEX_AFTER_MODECHANGE "G28X"
+#endif
+
+/**
+ * Multi-Stepper / Multi-Endstop
+ *
+ * When X2_DRIVER_TYPE is defined, this indicates that the X and X2 motors work in tandem.
+ * The following explanations for X also apply to Y and Z multi-stepper setups.
+ * Endstop offsets may be changed by 'M666 X Y Z' and stored to EEPROM.
+ *
+ * - Enable INVERT_X2_VS_X_DIR if the X2 motor requires an opposite DIR signal from X.
+ *
+ * - Enable X_DUAL_ENDSTOPS if the second motor has its own endstop, with adjustable offset.
+ *
+ *   - Extra endstops are included in the output of 'M119'.
+ *
+ *   - Set X_DUAL_ENDSTOP_ADJUSTMENT to the known error in the X2 endstop.
+ *     Applied to the X2 motor on 'G28' / 'G28 X'.
+ *     Get the offset by homing X and measuring the error.
+ *     Also set with 'M666 X' and stored to EEPROM with 'M500'.
+ *
+ *   - Use X2_USE_ENDSTOP to set the endstop plug by name. (_XMIN_, _XMAX_, _YMIN_, _YMAX_, _ZMIN_, _ZMAX_)
+ */
+#if HAS_X2_STEPPER && DISABLED(DUAL_X_CARRIAGE)
+  //#define INVERT_X2_VS_X_DIR        // X2 direction signal is the opposite of X
+  //#define X_DUAL_ENDSTOPS           // X2 has its own endstop
+  #if ENABLED(X_DUAL_ENDSTOPS)
+    #define X2_USE_ENDSTOP    _XMAX_  // X2 endstop board plug. Don't forget to enable USE_*_PLUG.
+    #define X2_ENDSTOP_ADJUSTMENT  0  // X2 offset relative to X endstop
+  #endif
+#endif
+
+#if HAS_DUAL_Y_STEPPERS
+  //#define INVERT_Y2_VS_Y_DIR        // Y2 direction signal is the opposite of Y
+  //#define Y_DUAL_ENDSTOPS           // Y2 has its own endstop
+  #if ENABLED(Y_DUAL_ENDSTOPS)
+    #define Y2_USE_ENDSTOP    _YMAX_  // Y2 endstop board plug. Don't forget to enable USE_*_PLUG.
+    #define Y2_ENDSTOP_ADJUSTMENT  0  // Y2 offset relative to Y endstop
+  #endif
+#endif
+
+//
+// Multi-Z steppers
+//
+#ifdef Z2_DRIVER_TYPE
+  //#define INVERT_Z2_VS_Z_DIR        // Z2 direction signal is the opposite of Z
+
+  //#define Z_MULTI_ENDSTOPS          // Other Z axes have their own endstops
+  #if ENABLED(Z_MULTI_ENDSTOPS)
+    #define Z2_USE_ENDSTOP   _XMAX_   // Z2 endstop board plug. Don't forget to enable USE_*_PLUG.
+    #define Z2_ENDSTOP_ADJUSTMENT 0   // Z2 offset relative to Y endstop
+  #endif
+  #ifdef Z3_DRIVER_TYPE
+    //#define INVERT_Z3_VS_Z_DIR      // Z3 direction signal is the opposite of Z
+    #if ENABLED(Z_MULTI_ENDSTOPS)
+      #define Z3_USE_ENDSTOP   _YMAX_ // Z3 endstop board plug. Don't forget to enable USE_*_PLUG.
+      #define Z3_ENDSTOP_ADJUSTMENT 0 // Z3 offset relative to Y endstop
+    #endif
+  #endif
+  #ifdef Z4_DRIVER_TYPE
+    //#define INVERT_Z4_VS_Z_DIR      // Z4 direction signal is the opposite of Z
+    #if ENABLED(Z_MULTI_ENDSTOPS)
+      #define Z4_USE_ENDSTOP   _ZMAX_ // Z4 endstop board plug. Don't forget to enable USE_*_PLUG.
+      #define Z4_ENDSTOP_ADJUSTMENT 0 // Z4 offset relative to Y endstop
+    #endif
+  #endif
+#endif
+
+// Drive the E axis with two synchronized steppers
+//#define E_DUAL_STEPPER_DRIVERS
+#if ENABLED(E_DUAL_STEPPER_DRIVERS)
+  //#define INVERT_E1_VS_E0_DIR       // E direction signals are opposites
 #endif
 
 // Activate a solenoid on the active extruder with M380. Disable all with M381.
@@ -651,15 +875,17 @@
  * the position of the toolhead relative to the workspace.
  */
 
-//#define SENSORLESS_BACKOFF_MM  { 2, 2 }     // (mm) Backoff from endstops before sensorless homing
+//#define SENSORLESS_BACKOFF_MM  { 2, 2, 0 }  // (linear=mm, rotational=°) Backoff from endstops before sensorless homing
 
-#define HOMING_BUMP_MM      { 5, 5, 2 }       // (mm) Backoff from endstops after first bump
+#define HOMING_BUMP_MM      { 5, 5, 2 }       // (linear=mm, rotational=°) Backoff from endstops after first bump
 #define HOMING_BUMP_DIVISOR { 2, 2, 4 }       // Re-Bump Speed Divisor (Divides the Homing Feedrate)
 
-//#define HOMING_BACKOFF_POST_MM { 2, 2, 2 }  // (mm) Backoff from endstops after homing
+//#define HOMING_BACKOFF_POST_MM { 2, 2, 2 }  // (linear=mm, rotational=°) Backoff from endstops after homing
+//#define XY_COUNTERPART_BACKOFF_MM 0         // (mm) Backoff X after homing Y, and vice-versa
 
 //#define QUICK_HOME                          // If G28 contains XY do a diagonal move first
 //#define HOME_Y_BEFORE_X                     // If G28 contains XY home Y before X
+//#define HOME_Z_FIRST                        // Home Z first. Requires a Z-MIN endstop (not a probe).
 //#define CODEPENDENT_XY_HOMING               // If X/Y can't home without homing Y/X first
 
 // @section bltouch
@@ -719,12 +945,14 @@
   //#define BLTOUCH_FORCE_MODE_SET
 
   /**
-   * Use "HIGH SPEED" mode for probing.
+   * Enable "HIGH SPEED" option for probing.
    * Danger: Disable if your probe sometimes fails. Only suitable for stable well-adjusted systems.
-   * This feature was designed for Delta's with very fast Z moves however higher speed cartesians may function
-   * If the machine cannot raise the probe fast enough after a trigger, it may enter a fault state.
+   * This feature was designed for Deltabots with very fast Z moves; however, higher speed Cartesians
+   * might be able to use it. If the machine can't raise Z fast enough the BLTouch may go into ALARM.
+   *
+   * Set the default state here, change with 'M401 S' or UI, use M500 to save, M502 to reset.
    */
-  //#define BLTOUCH_HS_MODE
+  //#define BLTOUCH_HS_MODE true
 
   // Safety: Enable voltage mode settings in the LCD menu.
   //#define BLTOUCH_LCD_VOLTAGE_MENU
@@ -739,9 +967,12 @@
  */
 //#define Z_STEPPER_AUTO_ALIGN
 #if ENABLED(Z_STEPPER_AUTO_ALIGN)
-  // Define probe X and Y positions for Z1, Z2 [, Z3 [, Z4]]
-  // If not defined, probe limits will be used.
-  // Override with 'M422 S X Y'
+  /**
+   * Define probe X and Y positions for Z1, Z2 [, Z3 [, Z4]]
+   * These positions are machine-relative and do not shift with the M206 home offset!
+   * If not defined, probe limits will be used.
+   * Override with 'M422 S X Y'.
+   */
   //#define Z_STEPPER_ALIGN_XY { {  10, 190 }, { 100,  10 }, { 190, 190 } }
 
   /**
@@ -767,19 +998,20 @@
     //#define Z_STEPPERS_ORIENTATION 0
   #endif
 
-  // Provide Z stepper positions for more rapid convergence in bed alignment.
-  // Requires triple stepper drivers (i.e., set NUM_Z_STEPPER_DRIVERS to 3)
-  //#define Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS
-  #if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
-    // Define Stepper XY positions for Z1, Z2, Z3 corresponding to
-    // the Z screw positions in the bed carriage.
-    // Define one position per Z stepper in stepper driver order.
-    #define Z_STEPPER_ALIGN_STEPPER_XY { { 210.7, 102.5 }, { 152.6, 220.0 }, { 94.5, 102.5 } }
-  #else
-    // Amplification factor. Used to scale the correction step up or down.
-    // In case the stepper (spindle) position is further out than the test point.
-    // Use a value > 1. NOTE: This may cause instability
-    #define Z_STEPPER_ALIGN_AMP 1.0
+  /**
+   * Z Stepper positions for more rapid convergence in bed alignment.
+   * Requires 3 or 4 Z steppers.
+   *
+   * Define Stepper XY positions for Z1, Z2, Z3... corresponding to the screw
+   * positions in the bed carriage, with one position per Z stepper in stepper
+   * driver order.
+   */
+  //#define Z_STEPPER_ALIGN_STEPPER_XY { { 210.7, 102.5 }, { 152.6, 220.0 }, { 94.5, 102.5 } }
+
+  #ifndef Z_STEPPER_ALIGN_STEPPER_XY
+    // Amplification factor. Used to scale the correction step up or down in case
+    // the stepper (spindle) position is farther out than the test point.
+    #define Z_STEPPER_ALIGN_AMP 1.0       // Use a value > 1.0 NOTE: This may cause instability!
   #endif
 
   #define G34_MAX_GRADE              5    // (%) Maximum incline that G34 will handle
@@ -797,10 +1029,10 @@
 //#define ASSISTED_TRAMMING
 #if ENABLED(ASSISTED_TRAMMING)
 
-  // Define positions for probing points, use the hotend as reference not the sensor.
-  #define TRAMMING_POINT_XY { {  20, 20 }, { 200,  20 }, { 200, 200 }, { 20, 200 } }
+  // Define positions for probe points.
+  #define TRAMMING_POINT_XY { {  20, 20 }, { 180,  20 }, { 180, 180 }, { 20, 180 } }
 
-  // Define positions names for probing points.
+  // Define position names for probe points.
   #define TRAMMING_POINT_NAME_1 "Front-Left"
   #define TRAMMING_POINT_NAME_2 "Front-Right"
   #define TRAMMING_POINT_NAME_3 "Back-Right"
@@ -808,7 +1040,10 @@
 
   #define RESTORE_LEVELING_AFTER_G35    // Enable to restore leveling setup after operation
   //#define REPORT_TRAMMING_MM          // Report Z deviation (mm) for each point relative to the first
-  //#define ASSISTED_TRAMMING_MENU_ITEM // Add a menu item for Assisted Tramming
+
+  //#define ASSISTED_TRAMMING_WIZARD    // Add a Tramming Wizard to the LCD menu
+
+  //#define ASSISTED_TRAMMING_WAIT_POSITION { X_CENTER, Y_CENTER, 30 } // Move the nozzle out of the way for adjustment
 
   /**
    * Screw thread:
@@ -822,6 +1057,43 @@
 
 // @section motion
 
+/**
+ * Input Shaping -- EXPERIMENTAL
+ *
+ * Zero Vibration (ZV) Input Shaping for X and/or Y movements.
+ *
+ * This option uses a lot of SRAM for the step buffer. The buffer size is
+ * calculated automatically from SHAPING_FREQ_[XY], DEFAULT_AXIS_STEPS_PER_UNIT,
+ * DEFAULT_MAX_FEEDRATE and ADAPTIVE_STEP_SMOOTHING. The default calculation can
+ * be overridden by setting SHAPING_MIN_FREQ and/or SHAPING_MAX_FEEDRATE.
+ * The higher the frequency and the lower the feedrate, the smaller the buffer.
+ * If the buffer is too small at runtime, input shaping will have reduced
+ * effectiveness during high speed movements.
+ *
+ * Tune with M593 D F:
+ *
+ *  D    Set the zeta/damping factor. If axes (X, Y, etc.) are not specified, set for all axes.
+ *  F Set the frequency. If axes (X, Y, etc.) are not specified, set for all axes.
+ *  T[map]       Input Shaping type, 0:ZV, 1:EI, 2:2H EI (not implemented yet)
+ *  X<1>         Set the given parameters only for the X axis.
+ *  Y<1>         Set the given parameters only for the Y axis.
+ */
+//#define INPUT_SHAPING_X
+//#define INPUT_SHAPING_Y
+#if EITHER(INPUT_SHAPING_X, INPUT_SHAPING_Y)
+  #if ENABLED(INPUT_SHAPING_X)
+    #define SHAPING_FREQ_X  40          // (Hz) The default dominant resonant frequency on the X axis.
+    #define SHAPING_ZETA_X  0.15f       // Damping ratio of the X axis (range: 0.0 = no damping to 1.0 = critical damping).
+  #endif
+  #if ENABLED(INPUT_SHAPING_Y)
+    #define SHAPING_FREQ_Y  40          // (Hz) The default dominant resonant frequency on the Y axis.
+    #define SHAPING_ZETA_Y  0.15f       // Damping ratio of the Y axis (range: 0.0 = no damping to 1.0 = critical damping).
+  #endif
+  //#define SHAPING_MIN_FREQ  20        // By default the minimum of the shaping frequencies. Override to affect SRAM usage.
+  //#define SHAPING_MAX_STEPRATE 10000  // By default the maximum total step rate of the shaped axes. Override to affect SRAM usage.
+  //#define SHAPING_MENU                // Add a menu to the LCD to set shaping parameters.
+#endif
+
 #define AXIS_RELATIVE_MODES { false, false, false, false }
 
 // Add a Duplicate option for well-separated conjoined nozzles
@@ -831,6 +1103,12 @@
 #define INVERT_X_STEP_PIN false
 #define INVERT_Y_STEP_PIN false
 #define INVERT_Z_STEP_PIN false
+#define INVERT_I_STEP_PIN false
+#define INVERT_J_STEP_PIN false
+#define INVERT_K_STEP_PIN false
+#define INVERT_U_STEP_PIN false
+#define INVERT_V_STEP_PIN false
+#define INVERT_W_STEP_PIN false
 #define INVERT_E_STEP_PIN false
 
 /**
@@ -842,16 +1120,17 @@
 #define DISABLE_INACTIVE_X true
 #define DISABLE_INACTIVE_Y true
 #define DISABLE_INACTIVE_Z true  // Set 'false' if the nozzle could fall onto your printed part!
+#define DISABLE_INACTIVE_I true
+#define DISABLE_INACTIVE_J true
+#define DISABLE_INACTIVE_K true
+#define DISABLE_INACTIVE_U true
+#define DISABLE_INACTIVE_V true
+#define DISABLE_INACTIVE_W true
 #define DISABLE_INACTIVE_E true
 
-// If the Nozzle or Bed falls when the Z stepper is disabled, set its resting position here.
-//#define Z_AFTER_DEACTIVATE Z_HOME_POS
-
-//#define HOME_AFTER_DEACTIVATE  // Require rehoming after steppers are deactivated
-
 // Default Minimum Feedrates for printing and travel moves
-#define DEFAULT_MINIMUMFEEDRATE       0.0     // (mm/s) Minimum feedrate. Set with M205 S.
-#define DEFAULT_MINTRAVELFEEDRATE     0.0     // (mm/s) Minimum travel feedrate. Set with M205 T.
+#define DEFAULT_MINIMUMFEEDRATE       0.0     // (mm/s. °/s for rotational-only moves) Minimum feedrate. Set with M205 S.
+#define DEFAULT_MINTRAVELFEEDRATE     0.0     // (mm/s. °/s for rotational-only moves) Minimum travel feedrate. Set with M205 T.
 
 // Minimum time that a segment needs to take as the buffer gets emptied
 #define DEFAULT_MINSEGMENTTIME        20000   // (µs) Set with M205 B.
@@ -887,9 +1166,12 @@
 #if ENABLED(BACKLASH_COMPENSATION)
   // Define values for backlash distance and correction.
   // If BACKLASH_GCODE is enabled these values are the defaults.
-  #define BACKLASH_DISTANCE_MM { 0, 0, 0 } // (mm)
+  #define BACKLASH_DISTANCE_MM { 0, 0, 0 } // (linear=mm, rotational=°) One value for each linear axis
   #define BACKLASH_CORRECTION    0.0       // 0.0 = no correction; 1.0 = full correction
 
+  // Add steps for motor direction changes on CORE kinematics
+  //#define CORE_BACKLASH
+
   // Set BACKLASH_SMOOTHING_MM to spread backlash correction over multiple segments
   // to reduce print artifacts. (Enabling this is costly in memory and computation!)
   //#define BACKLASH_SMOOTHING_MM 3 // (mm)
@@ -907,13 +1189,13 @@
       // increments while checking for the contact to be broken.
       #define BACKLASH_MEASUREMENT_LIMIT       0.5   // (mm)
       #define BACKLASH_MEASUREMENT_RESOLUTION  0.005 // (mm)
-      #define BACKLASH_MEASUREMENT_FEEDRATE    Z_PROBE_SPEED_SLOW // (mm/min)
+      #define BACKLASH_MEASUREMENT_FEEDRATE    Z_PROBE_FEEDRATE_SLOW // (mm/min)
     #endif
   #endif
 #endif
 
 /**
- * Automatic backlash, position and hotend offset calibration
+ * Automatic backlash, position, and hotend offset calibration
  *
  * Enable G425 to run automatic calibration using an electrically-
  * conductive cube, bolt, or washer mounted on the bed.
@@ -955,6 +1237,19 @@
   #define CALIBRATION_MEASURE_LEFT
   #define CALIBRATION_MEASURE_BACK
 
+  //#define CALIBRATION_MEASURE_IMIN
+  //#define CALIBRATION_MEASURE_IMAX
+  //#define CALIBRATION_MEASURE_JMIN
+  //#define CALIBRATION_MEASURE_JMAX
+  //#define CALIBRATION_MEASURE_KMIN
+  //#define CALIBRATION_MEASURE_KMAX
+  //#define CALIBRATION_MEASURE_UMIN
+  //#define CALIBRATION_MEASURE_UMAX
+  //#define CALIBRATION_MEASURE_VMIN
+  //#define CALIBRATION_MEASURE_VMAX
+  //#define CALIBRATION_MEASURE_WMIN
+  //#define CALIBRATION_MEASURE_WMAX
+
   // Probing at the exact top center only works if the center is flat. If
   // probing on a screwhead or hollow washer, probe near the edges.
   //#define CALIBRATION_MEASURE_AT_TOP_EDGES
@@ -1017,7 +1312,7 @@
 /**
  * I2C-based DIGIPOTs (e.g., Azteeg X3 Pro)
  */
-//#define DIGIPOT_MCP4018             // Requires https://github.com/stawel/SlowSoftI2CMaster
+//#define DIGIPOT_MCP4018             // Requires https://github.com/felias-fogg/SlowSoftI2CMaster
 //#define DIGIPOT_MCP4451
 #if EITHER(DIGIPOT_MCP4018, DIGIPOT_MCP4451)
   #define DIGIPOT_I2C_NUM_CHANNELS 8  // 5DPRINT:4   AZTEEG_X3_PRO:8   MKS_SBASE:5   MIGHTYBOARD_REVE:5
@@ -1048,10 +1343,10 @@
 
 // @section lcd
 
-#if EITHER(ULTIPANEL, EXTENSIBLE_UI)
+#if HAS_MANUAL_MOVE_MENU
   #define MANUAL_FEEDRATE { 50*60, 50*60, 4*60, 2*60 } // (mm/min) Feedrates for manual moves along X, Y, Z, E from panel
-  #define SHORT_MANUAL_Z_MOVE 0.025 // (mm) Smallest manual Z move (< 0.1mm)
-  #if ENABLED(ULTIPANEL)
+  #define FINE_MANUAL_MOVE 0.025    // (mm) Smallest manual move (< 0.1mm) applying to Z on most machines
+  #if IS_ULTIPANEL
     #define MANUAL_E_MOVES_RELATIVE // Display extruder move distance rather than "position"
     #define ULTIPANEL_FEEDMULTIPLY  // Encoder sets the feedrate multiplier on the Status Screen
   #endif
@@ -1071,14 +1366,45 @@
   #define FEEDRATE_CHANGE_BEEP_FREQUENCY 440
 #endif
 
-#if HAS_LCD_MENU
+//
+// LCD Backlight Timeout
+//
+//#define LCD_BACKLIGHT_TIMEOUT_MINS 1  // (minutes) Timeout before turning off the backlight
+
+#if HAS_BED_PROBE && EITHER(HAS_MARLINUI_MENU, HAS_TFT_LVGL_UI)
+  //#define PROBE_OFFSET_WIZARD       // Add a Probe Z Offset calibration option to the LCD menu
+  #if ENABLED(PROBE_OFFSET_WIZARD)
+    /**
+     * Enable to init the Probe Z-Offset when starting the Wizard.
+     * Use a height slightly above the estimated nozzle-to-probe Z offset.
+     * For example, with an offset of -5, consider a starting height of -4.
+     */
+    //#define PROBE_OFFSET_WIZARD_START_Z -4.0
+
+    // Set a convenient position to do the calibration (probing point and nozzle/bed-distance)
+    //#define PROBE_OFFSET_WIZARD_XY_POS { X_CENTER, Y_CENTER }
+  #endif
+#endif
+
+#if HAS_MARLINUI_MENU
 
-  // Add Probe Z Offset calibration to the Z Probe Offsets menu
   #if HAS_BED_PROBE
-    //#define PROBE_OFFSET_WIZARD
-    #if ENABLED(PROBE_OFFSET_WIZARD)
-      #define PROBE_OFFSET_START -4.0   // Estimated nozzle-to-probe Z offset, plus a little extra
+    // Add calibration in the Probe Offsets menu to compensate for X-axis twist.
+    //#define X_AXIS_TWIST_COMPENSATION
+    #if ENABLED(X_AXIS_TWIST_COMPENSATION)
+      /**
+       * Enable to init the Probe Z-Offset when starting the Wizard.
+       * Use a height slightly above the estimated nozzle-to-probe Z offset.
+       * For example, with an offset of -5, consider a starting height of -4.
+       */
+      #define XATC_START_Z 0.0
+      #define XATC_MAX_POINTS 3             // Number of points to probe in the wizard
+      #define XATC_Y_POSITION Y_CENTER      // (mm) Y position to probe
+      #define XATC_Z_OFFSETS { 0, 0, 0 }    // Z offsets for X axis sample points
     #endif
+
+    // Show Deploy / Stow Probe options in the Motion menu.
+    #define PROBE_DEPLOY_STOW_MENU
   #endif
 
   // Include a page of printer information in the LCD Main Menu
@@ -1090,6 +1416,39 @@
   // BACK menu items keep the highlight at the top
   //#define TURBO_BACK_MENU_ITEM
 
+  // Insert a menu for preheating at the top level to allow for quick access
+  //#define PREHEAT_SHORTCUT_MENU_ITEM
+
+#endif // HAS_MARLINUI_MENU
+
+#if ANY(HAS_DISPLAY, DWIN_LCD_PROUI, DWIN_CREALITY_LCD_JYERSUI)
+  //#define SOUND_MENU_ITEM   // Add a mute option to the LCD menu
+  #define SOUND_ON_DEFAULT    // Buzzer/speaker default enabled state
+#endif
+
+#if EITHER(HAS_DISPLAY, DWIN_LCD_PROUI)
+  // The timeout to return to the status screen from sub-menus
+  //#define LCD_TIMEOUT_TO_STATUS 15000   // (ms)
+
+  #if ENABLED(SHOW_BOOTSCREEN)
+    #define BOOTSCREEN_TIMEOUT 4000       // (ms) Total Duration to display the boot screen(s)
+    #if EITHER(HAS_MARLINUI_U8GLIB, TFT_COLOR_UI)
+      #define BOOT_MARLIN_LOGO_SMALL      // Show a smaller Marlin logo on the Boot Screen (saving lots of flash)
+    #endif
+  #endif
+
+  // Scroll a longer status message into view
+  //#define STATUS_MESSAGE_SCROLLING
+
+  // Apply a timeout to low-priority status messages
+  //#define STATUS_MESSAGE_TIMEOUT_SEC 30 // (seconds)
+
+  // On the Info Screen, display XY with one decimal place when possible
+  //#define LCD_DECIMAL_SMALL_XY
+
+  // Show the E position (filament used) during printing
+  //#define LCD_SHOW_E_TOTAL
+
   /**
    * LED Control Menu
    * Add LED Control to the LCD menu
@@ -1107,53 +1466,45 @@
       //#define LED_USER_PRESET_STARTUP       // Have the printer display the user preset color on startup
     #endif
     #if ENABLED(NEO2_COLOR_PRESETS)
-      #define NEO2_USER_PRESET_RED        255  // User defined RED value
-      #define NEO2_USER_PRESET_GREEN      128  // User defined GREEN value
-      #define NEO2_USER_PRESET_BLUE         0  // User defined BLUE value
-      #define NEO2_USER_PRESET_WHITE      255  // User defined WHITE value
-      #define NEO2_USER_PRESET_BRIGHTNESS 255  // User defined intensity
-      //#define NEO2_USER_PRESET_STARTUP       // Have the printer display the user preset color on startup for the second strip
+      #define NEO2_USER_PRESET_RED        255 // User defined RED value
+      #define NEO2_USER_PRESET_GREEN      128 // User defined GREEN value
+      #define NEO2_USER_PRESET_BLUE         0 // User defined BLUE value
+      #define NEO2_USER_PRESET_WHITE      255 // User defined WHITE value
+      #define NEO2_USER_PRESET_BRIGHTNESS 255 // User defined intensity
+      //#define NEO2_USER_PRESET_STARTUP      // Have the printer display the user preset color on startup for the second strip
     #endif
   #endif
 
-#endif // HAS_LCD_MENU
+#endif // HAS_DISPLAY || DWIN_LCD_PROUI
 
-// Scroll a longer status message into view
-//#define STATUS_MESSAGE_SCROLLING
-
-// On the Info Screen, display XY with one decimal place when possible
-//#define LCD_DECIMAL_SMALL_XY
-
-// The timeout (in ms) to return to the status screen from sub-menus
-//#define LCD_TIMEOUT_TO_STATUS 15000
-
-// Add an 'M73' G-code to set the current percentage
-//#define LCD_SET_PROGRESS_MANUALLY
-
-// Show the E position (filament used) during printing
-//#define LCD_SHOW_E_TOTAL
-
-#if ENABLED(SHOW_BOOTSCREEN)
-  #define BOOTSCREEN_TIMEOUT 4000        // (ms) Total Duration to display the boot screen(s)
+// Add 'M73' to set print job progress, overrides Marlin's built-in estimate
+//#define SET_PROGRESS_MANUALLY
+#if ENABLED(SET_PROGRESS_MANUALLY)
+  #define SET_PROGRESS_PERCENT            // Add 'P' parameter to set percentage done
+  #define SET_REMAINING_TIME              // Add 'R' parameter to set remaining time
+  //#define SET_INTERACTION_TIME          // Add 'C' parameter to set time until next filament change or other user interaction
+  //#define M73_REPORT                    // Report M73 values to host
+  #if BOTH(M73_REPORT, SDSUPPORT)
+    #define M73_REPORT_SD_ONLY            // Report only when printing from SD
+  #endif
 #endif
 
-#if EITHER(SDSUPPORT, LCD_SET_PROGRESS_MANUALLY) && ANY(HAS_MARLINUI_U8GLIB, HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
-  //#define SHOW_REMAINING_TIME       // Display estimated time to completion
-  #if ENABLED(SHOW_REMAINING_TIME)
-    //#define USE_M73_REMAINING_TIME  // Use remaining time from M73 command instead of estimation
-    //#define ROTATE_PROGRESS_DISPLAY // Display (P)rogress, (E)lapsed, and (R)emaining time
-  #endif
-
-  #if HAS_MARLINUI_U8GLIB
-    //#define PRINT_PROGRESS_SHOW_DECIMALS // Show progress with decimal digits
+// LCD Print Progress options. Multiple times may be displayed in turn.
+#if HAS_DISPLAY && EITHER(SDSUPPORT, SET_PROGRESS_MANUALLY)
+  #define SHOW_PROGRESS_PERCENT           // Show print progress percentage (doesn't affect progress bar)
+  #define SHOW_ELAPSED_TIME               // Display elapsed printing time (prefix 'E')
+  //#define SHOW_REMAINING_TIME           // Display estimated time to completion (prefix 'R')
+  #if ENABLED(SET_INTERACTION_TIME)
+    #define SHOW_INTERACTION_TIME         // Display time until next user interaction ('C' = filament change)
   #endif
+  //#define PRINT_PROGRESS_SHOW_DECIMALS  // Show/report progress with decimal digits, not all UIs support this
 
   #if EITHER(HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
     //#define LCD_PROGRESS_BAR            // Show a progress bar on HD44780 LCDs for SD printing
     #if ENABLED(LCD_PROGRESS_BAR)
       #define PROGRESS_BAR_BAR_TIME 2000  // (ms) Amount of time to show the bar
       #define PROGRESS_BAR_MSG_TIME 3000  // (ms) Amount of time to show the status message
-      #define PROGRESS_MSG_EXPIRE   0     // (ms) Amount of time to retain the status message (0=forever)
+      #define PROGRESS_MSG_EXPIRE      0  // (ms) Amount of time to retain the status message (0=forever)
       //#define PROGRESS_MSG_ONCE         // Show the message for MSG_TIME then clear it
       //#define LCD_PROGRESS_BAR_TEST     // Add a menu item to test the progress bar
     #endif
@@ -1161,13 +1512,26 @@
 #endif
 
 #if ENABLED(SDSUPPORT)
+  /**
+   * SD Card SPI Speed
+   * May be required to resolve "volume init" errors.
+   *
+   * Enable and set to SPI_HALF_SPEED, SPI_QUARTER_SPEED, or SPI_EIGHTH_SPEED
+   *  otherwise full speed will be applied.
+   *
+   * :['SPI_HALF_SPEED', 'SPI_QUARTER_SPEED', 'SPI_EIGHTH_SPEED']
+   */
+  //#define SD_SPI_SPEED SPI_HALF_SPEED
 
   // The standard SD detect circuit reads LOW when media is inserted and HIGH when empty.
   // Enable this option and set to HIGH if your SD cards are incorrectly detected.
   //#define SD_DETECT_STATE HIGH
 
+  //#define SD_IGNORE_AT_STARTUP            // Don't mount the SD card when starting up
   //#define SDCARD_READONLY                 // Read-only SD card (to save over 2K of flash)
 
+  //#define GCODE_REPEAT_MARKERS            // Enable G-code M808 to set repeat markers and do looping
+
   #define SD_PROCEDURE_DEPTH 1              // Increase if you need more nested M32 calls
 
   #define SD_FINISHED_STEPPERRELEASE true   // Disable steppers when SD Print is finished
@@ -1179,8 +1543,13 @@
 
   #define SD_MENU_CONFIRM_START             // Confirm the selected SD file before printing
 
+  //#define NO_SD_AUTOSTART                 // Remove auto#.g file support completely to save some Flash, SRAM
   //#define MENU_ADDAUTOSTART               // Add a menu option to run auto#.g files
 
+  //#define BROWSE_MEDIA_ON_INSERT          // Open the file browser when media is inserted
+
+  //#define MEDIA_MENU_AT_TOP               // Force the media menu to be listed on the top of the main menu
+
   #define EVENT_GCODE_SD_ABORT "G28XY"      // G-code to run on SD Abort Print (e.g., "G28XY" or "G27")
 
   #if ENABLED(PRINTER_EVENT_LEDS)
@@ -1199,17 +1568,23 @@
   #if ENABLED(POWER_LOSS_RECOVERY)
     #define PLR_ENABLED_DEFAULT   false // Power Loss Recovery enabled by default. (Set with 'M413 Sn' & M500)
     //#define BACKUP_POWER_SUPPLY       // Backup power / UPS to move the steppers on power loss
-    //#define POWER_LOSS_RECOVER_ZHOME  // Z homing is needed for proper recovery. 99.9% of the time this should be disabled!
     //#define POWER_LOSS_ZRAISE       2 // (mm) Z axis raise on resume (on power loss with UPS)
     //#define POWER_LOSS_PIN         44 // Pin to detect power loss. Set to -1 to disable default pin on boards without module.
     //#define POWER_LOSS_STATE     HIGH // State of pin indicating power loss
-    //#define POWER_LOSS_PULL           // Set pullup / pulldown as appropriate
+    //#define POWER_LOSS_PULLUP         // Set pullup / pulldown as appropriate for your sensor
+    //#define POWER_LOSS_PULLDOWN
     //#define POWER_LOSS_PURGE_LEN   20 // (mm) Length of filament to purge on resume
     //#define POWER_LOSS_RETRACT_LEN 10 // (mm) Length of filament to retract on fail. Requires backup power.
 
     // Without a POWER_LOSS_PIN the following option helps reduce wear on the SD card,
     // especially with "vase mode" printing. Set too high and vases cannot be continued.
     #define POWER_LOSS_MIN_Z_CHANGE 0.05 // (mm) Minimum Z change before saving power-loss data
+
+    // Enable if Z homing is needed for proper recovery. 99.9% of the time this should be disabled!
+    //#define POWER_LOSS_RECOVER_ZHOME
+    #if ENABLED(POWER_LOSS_RECOVER_ZHOME)
+      //#define POWER_LOSS_ZHOME_POS { 0, 0 } // Safe XY position to home Z while avoiding objects on the bed
+    #endif
   #endif
 
   /**
@@ -1250,33 +1625,31 @@
                                       // Note: Only affects SCROLL_LONG_FILENAMES with SDSORT_CACHE_NAMES but not SDSORT_DYNAMIC_RAM.
   #endif
 
-  // This allows hosts to request long names for files and folders with M33
-  //#define LONG_FILENAME_HOST_SUPPORT
+  // Allow international symbols in long filenames. To display correctly, the
+  // LCD's font must contain the characters. Check your selected LCD language.
+  //#define UTF_FILENAME_SUPPORT
 
-  // Enable this option to scroll long filenames in the SD card menu
-  //#define SCROLL_LONG_FILENAMES
+  //#define LONG_FILENAME_HOST_SUPPORT    // Get the long filename of a file/folder with 'M33 ' and list long filenames with 'M20 L'
+  //#define LONG_FILENAME_WRITE_SUPPORT   // Create / delete files with long filenames via M28, M30, and Binary Transfer Protocol
+  //#define M20_TIMESTAMP_SUPPORT         // Include timestamps by adding the 'T' flag to M20 commands
 
-  // Leave the heaters on after Stop Print (not recommended!)
-  //#define SD_ABORT_NO_COOLDOWN
+  //#define SCROLL_LONG_FILENAMES         // Scroll long filenames in the SD card menu
+
+  //#define SD_ABORT_NO_COOLDOWN          // Leave the heaters on after Stop Print (not recommended!)
 
   /**
-   * This option allows you to abort SD printing when any endstop is triggered.
-   * This feature must be enabled with "M540 S1" or from the LCD menu.
-   * To have any effect, endstops must be enabled during SD printing.
+   * Abort SD printing when any endstop is triggered.
+   * This feature is enabled with 'M540 S1' or from the LCD menu.
+   * Endstops must be activated for this option to work.
    */
   //#define SD_ABORT_ON_ENDSTOP_HIT
+  #if ENABLED(SD_ABORT_ON_ENDSTOP_HIT)
+    //#define SD_ABORT_ON_ENDSTOP_HIT_GCODE "G28XY" // G-code to run on endstop hit (e.g., "G28XY" or "G27")
+  #endif
 
-  /**
-   * This option makes it easier to print the same SD Card file again.
-   * On print completion the LCD Menu will open with the file selected.
-   * You can just click to start the print, or navigate elsewhere.
-   */
-  //#define SD_REPRINT_LAST_SELECTED_FILE
+  //#define SD_REPRINT_LAST_SELECTED_FILE // On print completion open the LCD Menu and select the same file
 
-  /**
-   * Auto-report SdCard status with M27 S
-   */
-  //#define AUTO_REPORT_SD_STATUS
+  //#define AUTO_REPORT_SD_STATUS         // Auto-report media status with 'M27 S'
 
   /**
    * Support for USB thumb drives using an Arduino USB Host Shield or
@@ -1294,9 +1667,6 @@
    */
   //#define USB_FLASH_DRIVE_SUPPORT
   #if ENABLED(USB_FLASH_DRIVE_SUPPORT)
-    #define USB_CS_PIN    SDSS
-    #define USB_INTR_PIN  SD_DETECT_PIN
-
     /**
      * USB Host Shield Library
      *
@@ -1307,7 +1677,20 @@
      *   is less tested and is known to interfere with Servos.
      *   [1] This requires USB_INTR_PIN to be interrupt-capable.
      */
+    //#define USE_UHS2_USB
     //#define USE_UHS3_USB
+
+    #define DISABLE_DUE_SD_MMC // Disable USB Host access to USB Drive to prevent hangs on block access for DUE platform
+
+    /**
+     * Native USB Host supported by some boards (USB OTG)
+     */
+    //#define USE_OTG_USB_HOST
+
+    #if DISABLED(USE_OTG_USB_HOST)
+      #define USB_CS_PIN    SDSS
+      #define USB_INTR_PIN  SD_DETECT_PIN
+    #endif
   #endif
 
   /**
@@ -1326,20 +1709,63 @@
     #define SD_FIRMWARE_UPDATE_INACTIVE_VALUE 0xFF
   #endif
 
+  /**
+   * Enable this option if you have more than ~3K of unused flash space.
+   * Marlin will embed all settings in the firmware binary as compressed data.
+   * Use 'M503 C' to write the settings out to the SD Card as 'mc.zip'.
+   * See docs/ConfigEmbedding.md for details on how to use 'mc-apply.py'.
+   */
+  //#define CONFIGURATION_EMBEDDING
+
   // Add an optimized binary file transfer mode, initiated with 'M28 B1'
   //#define BINARY_FILE_TRANSFER
 
+  #if ENABLED(BINARY_FILE_TRANSFER)
+    // Include extra facilities (e.g., 'M20 F') supporting firmware upload via BINARY_FILE_TRANSFER
+    //#define CUSTOM_FIRMWARE_UPLOAD
+  #endif
+
   /**
    * Set this option to one of the following (or the board's defaults apply):
    *
    *           LCD - Use the SD drive in the external LCD controller.
-   *       ONBOARD - Use the SD drive on the control board. (No SD_DETECT_PIN. M21 to init.)
+   *       ONBOARD - Use the SD drive on the control board.
    *  CUSTOM_CABLE - Use a custom cable to access the SD (as defined in a pins file).
    *
    * :[ 'LCD', 'ONBOARD', 'CUSTOM_CABLE' ]
    */
   #define SDCARD_CONNECTION ONBOARD
 
+  // Enable if SD detect is rendered useless (e.g., by using an SD extender)
+  //#define NO_SD_DETECT
+
+  /**
+   * Multiple volume support - EXPERIMENTAL.
+   * Adds 'M21 Pm' / 'M21 S' / 'M21 U' to mount SD Card / USB Drive.
+   */
+  //#define MULTI_VOLUME
+  #if ENABLED(MULTI_VOLUME)
+    #define VOLUME_SD_ONBOARD
+    #define VOLUME_USB_FLASH_DRIVE
+    #define DEFAULT_VOLUME SV_SD_ONBOARD
+    #define DEFAULT_SHARED_VOLUME SV_USB_FLASH_DRIVE
+  #endif
+
+  // Enable if SD detect is rendered useless (e.g., by using an SD extender)
+  //#define NO_SD_DETECT
+
+  /**
+   * Multiple volume support - EXPERIMENTAL.
+   * Adds 'M21 Pm' / 'M21 S' / 'M21 U' to mount SD Card / USB Drive.
+   */
+  //#define MULTI_VOLUME
+  #if ENABLED(MULTI_VOLUME)
+    #define VOLUME_SD_ONBOARD
+    #define VOLUME_USB_FLASH_DRIVE
+    #define DEFAULT_VOLUME SV_SD_ONBOARD
+    #define DEFAULT_SHARED_VOLUME SV_USB_FLASH_DRIVE
+  #endif
+
 #endif // SDSUPPORT
 
 /**
@@ -1361,26 +1787,28 @@
  * printing performance versus fast display updates.
  */
 #if HAS_MARLINUI_U8GLIB
-  // Show SD percentage next to the progress bar
-  //#define DOGM_SD_PERCENT
-
   // Save many cycles by drawing a hollow frame or no frame on the Info Screen
   //#define XYZ_NO_FRAME
   #define XYZ_HOLLOW_FRAME
 
-  // Enable to save many cycles by drawing a hollow frame on Menu Screens
-  #define MENU_HOLLOW_FRAME
-
-  // A bigger font is available for edit items. Costs 3120 bytes of PROGMEM.
+  // A bigger font is available for edit items. Costs 3120 bytes of flash.
   // Western only. Not available for Cyrillic, Kana, Turkish, Greek, or Chinese.
   //#define USE_BIG_EDIT_FONT
 
-  // A smaller font may be used on the Info Screen. Costs 2434 bytes of PROGMEM.
+  // A smaller font may be used on the Info Screen. Costs 2434 bytes of flash.
   // Western only. Not available for Cyrillic, Kana, Turkish, Greek, or Chinese.
   //#define USE_SMALL_INFOFONT
 
-  // Swap the CW/CCW indicators in the graphics overlay
-  //#define OVERLAY_GFX_REVERSE
+  /**
+   * Graphical Display Sleep
+   *
+   * The U8G library provides sleep / wake functions for SH1106, SSD1306,
+   * SSD1309, and some other DOGM displays.
+   * Enable this option to save energy and prevent OLED pixel burn-in.
+   * Adds the menu item Configuration > LCD Timeout (m) to set a wait period
+   * from 0 (disabled) to 99 minutes.
+   */
+  //#define DISPLAY_SLEEP_MINUTES 2  // (minutes) Timeout before turning off the screen. Set with M255 S.
 
   /**
    * ST7920-based LCDs can emulate a 16 x 4 character display using
@@ -1394,7 +1822,7 @@
    * Set STATUS_EXPIRE_SECONDS to zero to never clear the status.
    * This will prevent position updates from being displayed.
    */
-  #if ENABLED(U8GLIB_ST7920)
+  #if IS_U8GLIB_ST7920
     // Enable this option and reduce the value to optimize screen updates.
     // The normal delay is 10µs. Use the lowest value that still gives a reliable display.
     //#define DOGM_SPI_DELAY_US 5
@@ -1412,17 +1840,18 @@
    */
   //#define STATUS_COMBINE_HEATERS    // Use combined heater images instead of separate ones
   //#define STATUS_HOTEND_NUMBERLESS  // Use plain hotend icons instead of numbered ones (with 2+ hotends)
-  #define STATUS_HOTEND_INVERTED      // Show solid nozzle bitmaps when heating (Requires STATUS_HOTEND_ANIM)
+  #define STATUS_HOTEND_INVERTED      // Show solid nozzle bitmaps when heating (Requires STATUS_HOTEND_ANIM for numbered hotends)
   #define STATUS_HOTEND_ANIM          // Use a second bitmap to indicate hotend heating
   #define STATUS_BED_ANIM             // Use a second bitmap to indicate bed heating
   //#define STATUS_CHAMBER_ANIM         // Use a second bitmap to indicate chamber heating
   //#define STATUS_CUTTER_ANIM        // Use a second bitmap to indicate spindle / laser active
+  //#define STATUS_COOLER_ANIM        // Use a second bitmap to indicate laser cooling
+  //#define STATUS_FLOWMETER_ANIM     // Use multiple bitmaps to indicate coolant flow
   //#define STATUS_ALT_BED_BITMAP     // Use the alternative bed bitmap
   //#define STATUS_ALT_FAN_BITMAP     // Use the alternative fan bitmap
   //#define STATUS_FAN_FRAMES 3       // :[0,1,2,3,4] Number of fan animation frames
   //#define STATUS_HEAT_PERCENT       // Show heating in a progress bar
-  #define BOOT_MARLIN_LOGO_SMALL    // Show a smaller Marlin logo on the Boot Screen (saving 399 bytes of flash)
-  //#define BOOT_MARLIN_LOGO_ANIMATED // Animated Marlin logo. Costs ~3260 (or ~940) bytes of PROGMEM.
+  //#define BOOT_MARLIN_LOGO_ANIMATED // Animated Marlin logo. Costs ~3260 (or ~940) bytes of flash.
 
   // Frivolous Game Options
   //#define MARLIN_BRICKOUT
@@ -1432,11 +1861,15 @@
 
 #endif // HAS_MARLINUI_U8GLIB
 
+#if HAS_MARLINUI_U8GLIB || IS_DWIN_MARLINUI
+  #define MENU_HOLLOW_FRAME           // Enable to save many cycles by drawing a hollow frame on Menu Screens
+  //#define OVERLAY_GFX_REVERSE       // Swap the CW/CCW indicators in the graphics overlay
+#endif
+
 //
 // Additional options for DGUS / DWIN displays
 //
 #if HAS_DGUS_LCD
-  #define LCD_SERIAL_PORT 3
   #define LCD_BAUDRATE 115200
 
   #define DGUS_RX_BUFFER_SIZE 128
@@ -1445,12 +1878,12 @@
 
   #define DGUS_UPDATE_INTERVAL_MS  500    // (ms) Interval between automatic screen updates
 
-  #if EITHER(DGUS_LCD_UI_FYSETC, DGUS_LCD_UI_HIPRECY)
+  #if ANY(DGUS_LCD_UI_FYSETC, DGUS_LCD_UI_MKS, DGUS_LCD_UI_HIPRECY)
     #define DGUS_PRINT_FILENAME           // Display the filename during printing
     #define DGUS_PREHEAT_UI               // Display a preheat screen during heatup
 
-    #if ENABLED(DGUS_LCD_UI_FYSETC)
-      //#define DGUS_UI_MOVE_DIS_OPTION   // Disabled by default for UI_FYSETC
+    #if EITHER(DGUS_LCD_UI_FYSETC, DGUS_LCD_UI_MKS)
+      //#define DGUS_UI_MOVE_DIS_OPTION   // Disabled by default for FYSETC and MKS
     #else
       #define DGUS_UI_MOVE_DIS_OPTION     // Enabled by default for UI_HIPRECY
     #endif
@@ -1469,6 +1902,44 @@
   #endif
 #endif // HAS_DGUS_LCD
 
+//
+// Additional options for AnyCubic Chiron TFT displays
+//
+#if ENABLED(ANYCUBIC_LCD_CHIRON)
+  // By default the type of panel is automatically detected.
+  // Enable one of these options if you know the panel type.
+  //#define CHIRON_TFT_STANDARD
+  //#define CHIRON_TFT_NEW
+
+  // Enable the longer Anycubic powerup startup tune
+  //#define AC_DEFAULT_STARTUP_TUNE
+
+  /**
+   * Display Folders
+   * By default the file browser lists all G-code files (including those in subfolders) in a flat list.
+   * Enable this option to display a hierarchical file browser.
+   *
+   * NOTES:
+   * - Without this option it helps to enable SDCARD_SORT_ALPHA so files are sorted before/after folders.
+   * - When used with the "new" panel, folder names will also have '.gcode' appended to their names.
+   *   This hack is currently required to force the panel to show folders.
+   */
+  #define AC_SD_FOLDER_VIEW
+#endif
+
+//
+// Specify additional languages for the UI. Default specified by LCD_LANGUAGE.
+//
+#if ANY(DOGLCD, TFT_COLOR_UI, TOUCH_UI_FTDI_EVE, IS_DWIN_MARLINUI)
+  //#define LCD_LANGUAGE_2 fr
+  //#define LCD_LANGUAGE_3 de
+  //#define LCD_LANGUAGE_4 es
+  //#define LCD_LANGUAGE_5 it
+  #ifdef LCD_LANGUAGE_2
+    //#define LCD_LANGUAGE_AUTO_SAVE // Automatically save language to EEPROM on change
+  #endif
+#endif
+
 //
 // Touch UI for the FTDI Embedded Video Engine (EVE)
 //
@@ -1478,8 +1949,10 @@
   //#define LCD_4DSYSTEMS_4DLCD_FT843 // 4D Systems 4.3" (480x272)
   //#define LCD_HAOYU_FT800CB         // Haoyu with 4.3" or 5" (480x272)
   //#define LCD_HAOYU_FT810CB         // Haoyu with 5" (800x480)
-  //#define LCD_ALEPHOBJECTS_CLCD_UI  // Aleph Objects Color LCD UI
+  //#define LCD_LULZBOT_CLCD_UI       // LulzBot Color LCD UI
   //#define LCD_FYSETC_TFT81050       // FYSETC with 5" (800x480)
+  //#define LCD_EVE3_50G              // Matrix Orbital 5.0", 800x480, BT815
+  //#define LCD_EVE2_50G              // Matrix Orbital 5.0", 800x480, FT813
 
   // Correct the resolution if not using the stock TFT panel.
   //#define TOUCH_UI_320x240
@@ -1487,8 +1960,8 @@
   //#define TOUCH_UI_800x480
 
   // Mappings for boards with a standard RepRapDiscount Display connector
-  //#define AO_EXP1_PINMAP      // AlephObjects CLCD UI EXP1 mapping
-  //#define AO_EXP2_PINMAP      // AlephObjects CLCD UI EXP2 mapping
+  //#define AO_EXP1_PINMAP      // LulzBot CLCD UI EXP1 mapping
+  //#define AO_EXP2_PINMAP      // LulzBot CLCD UI EXP2 mapping
   //#define CR10_TFT_PINMAP     // Rudolph Riedel's CR10 pin mapping
   //#define S6_TFT_PINMAP       // FYSETC S6 pin mapping
   //#define F6_TFT_PINMAP       // FYSETC F6 pin mapping
@@ -1536,18 +2009,14 @@
       //#define TOUCH_UI_UTF8_FRACTIONS     // ¼ ½ ¾
       //#define TOUCH_UI_UTF8_SYMBOLS       // µ ¶ ¦ § ¬
     #endif
+
+    // Cyrillic character set, costs about 27KiB of flash
+    //#define TOUCH_UI_UTF8_CYRILLIC_CHARSET
   #endif
 
   // Use a smaller font when labels don't fit buttons
   #define TOUCH_UI_FIT_TEXT
 
-  // Allow language selection from menu at run-time (otherwise use LCD_LANGUAGE)
-  //#define LCD_LANGUAGE_1 en
-  //#define LCD_LANGUAGE_2 fr
-  //#define LCD_LANGUAGE_3 de
-  //#define LCD_LANGUAGE_4 es
-  //#define LCD_LANGUAGE_5 it
-
   // Use a numeric passcode for "Screen lock" keypad.
   // (recommended for smaller displays)
   //#define TOUCH_UI_PASSCODE
@@ -1652,14 +2121,35 @@
  */
 //#define LIN_ADVANCE
 #if ENABLED(LIN_ADVANCE)
-  //#define EXTRA_LIN_ADVANCE_K // Enable for second linear advance constants
-  #define LIN_ADVANCE_K 0.22    // Unit: mm compression per 1mm/s extruder speed
-  //#define LA_DEBUG            // If enabled, this will generate debug information output over USB.
-  //#define EXPERIMENTAL_SCURVE // Enable this option to permit S-Curve Acceleration
+  #if ENABLED(DISTINCT_E_FACTORS)
+    #define ADVANCE_K { 0.22 }    // (mm) Compression length per 1mm/s extruder speed, per extruder
+  #else
+    #define ADVANCE_K 0.22        // (mm) Compression length applying to all extruders
+  #endif
+  //#define ADVANCE_K_EXTRA       // Add a second linear advance constant, configurable with M900 L.
+  //#define LA_DEBUG              // Print debug information to serial during operation. Disable for production use.
+  //#define EXPERIMENTAL_SCURVE   // Allow S-Curve Acceleration to be used with LA.
+  //#define ALLOW_LOW_EJERK       // Allow a DEFAULT_EJERK value of <10. Recommended for direct drive hotends.
+  //#define EXPERIMENTAL_I2S_LA   // Allow I2S_STEPPER_STREAM to be used with LA. Performance degrades as the LA step rate reaches ~20kHz.
 #endif
 
 // @section leveling
 
+/**
+ * Use Safe Bed Leveling coordinates to move axes to a useful position before bed probing.
+ * For example, after homing a rotational axis the Z probe might not be perpendicular to the bed.
+ * Choose values the orient the bed horizontally and the Z-probe vertically.
+ */
+//#define SAFE_BED_LEVELING_START_X 0.0
+//#define SAFE_BED_LEVELING_START_Y 0.0
+//#define SAFE_BED_LEVELING_START_Z 0.0
+//#define SAFE_BED_LEVELING_START_I 0.0
+//#define SAFE_BED_LEVELING_START_J 0.0
+//#define SAFE_BED_LEVELING_START_K 0.0
+//#define SAFE_BED_LEVELING_START_U 0.0
+//#define SAFE_BED_LEVELING_START_V 0.0
+//#define SAFE_BED_LEVELING_START_W 0.0
+
 /**
  * Points to probe for all 3-point Leveling procedures.
  * Override if the automatically selected points are inadequate.
@@ -1707,6 +2197,10 @@
   //#define MESH_MAX_Y Y_BED_SIZE - (MESH_INSET)
 #endif
 
+#if BOTH(AUTO_BED_LEVELING_UBL, EEPROM_SETTINGS)
+  //#define OPTIMIZED_MESH_STORAGE  // Store mesh with less precision to save EEPROM space
+#endif
+
 /**
  * Repeatedly attempt G29 leveling until it succeeds.
  * Stop after G29_MAX_RETRIES attempts.
@@ -1727,59 +2221,69 @@
 
 /**
  * Thermal Probe Compensation
- * Probe measurements are adjusted to compensate for temperature distortion.
- * Use G76 to calibrate this feature. Use M871 to set values manually.
- * For a more detailed explanation of the process see G76_M871.cpp.
+ *
+ * Adjust probe measurements to compensate for distortion associated with the temperature
+ * of the probe, bed, and/or hotend.
+ * Use G76 to automatically calibrate this feature for probe and bed temperatures.
+ * (Extruder temperature/offset values must be calibrated manually.)
+ * Use M871 to set temperature/offset values manually.
+ * For more details see https://marlinfw.org/docs/features/probe_temp_compensation.html
  */
-#if HAS_BED_PROBE && TEMP_SENSOR_PROBE && TEMP_SENSOR_BED
-  // Enable thermal first layer compensation using bed and probe temperatures
-  #define PROBE_TEMP_COMPENSATION
+//#define PTC_PROBE    // Compensate based on probe temperature
+//#define PTC_BED      // Compensate based on bed temperature
+//#define PTC_HOTEND   // Compensate based on hotend temperature
 
-  // Add additional compensation depending on hotend temperature
-  // Note: this values cannot be calibrated and have to be set manually
-  #if ENABLED(PROBE_TEMP_COMPENSATION)
+#if ANY(PTC_PROBE, PTC_BED, PTC_HOTEND)
+  /**
+   * If the probe is outside the defined range, use linear extrapolation with the closest
+   * point and the point with index PTC_LINEAR_EXTRAPOLATION. e.g., If set to 4 it will use the
+   * linear extrapolation between data[0] and data[4] for values below PTC_PROBE_START.
+   */
+  //#define PTC_LINEAR_EXTRAPOLATION 4
+
+  #if ENABLED(PTC_PROBE)
+    // Probe temperature calibration generates a table of values starting at PTC_PROBE_START
+    // (e.g., 30), in steps of PTC_PROBE_RES (e.g., 5) with PTC_PROBE_COUNT (e.g., 10) samples.
+    #define PTC_PROBE_START   30    // (°C)
+    #define PTC_PROBE_RES      5    // (°C)
+    #define PTC_PROBE_COUNT   10
+    #define PTC_PROBE_ZOFFS   { 0 } // (µm) Z adjustments per sample
+  #endif
+
+  #if ENABLED(PTC_BED)
+    // Bed temperature calibration builds a similar table.
+    #define PTC_BED_START     60    // (°C)
+    #define PTC_BED_RES        5    // (°C)
+    #define PTC_BED_COUNT     10
+    #define PTC_BED_ZOFFS     { 0 } // (µm) Z adjustments per sample
+  #endif
+
+  #if ENABLED(PTC_HOTEND)
+    // Note: There is no automatic calibration for the hotend. Use M871.
+    #define PTC_HOTEND_START 180    // (°C)
+    #define PTC_HOTEND_RES     5    // (°C)
+    #define PTC_HOTEND_COUNT  20
+    #define PTC_HOTEND_ZOFFS  { 0 } // (µm) Z adjustments per sample
+  #endif
+
+  // G76 options
+  #if BOTH(PTC_PROBE, PTC_BED)
     // Park position to wait for probe cooldown
     #define PTC_PARK_POS   { 0, 0, 100 }
 
     // Probe position to probe and wait for probe to reach target temperature
+    //#define PTC_PROBE_POS  { 12.0f, 7.3f } // Example: MK52 magnetic heatbed
     #define PTC_PROBE_POS  { 90, 100 }
 
-    // Enable additional compensation using hotend temperature
-    // Note: this values cannot be calibrated automatically but have to be set manually
-    //#define USE_TEMP_EXT_COMPENSATION
+    // The temperature the probe should be at while taking measurements during
+    // bed temperature calibration.
+    #define PTC_PROBE_TEMP    30  // (°C)
 
-    // Probe temperature calibration generates a table of values starting at PTC_SAMPLE_START
-    // (e.g. 30), in steps of PTC_SAMPLE_RES (e.g. 5) with PTC_SAMPLE_COUNT (e.g. 10) samples.
-
-    //#define PTC_SAMPLE_START  30.0f
-    //#define PTC_SAMPLE_RES    5.0f
-    //#define PTC_SAMPLE_COUNT  10U
-
-    // Bed temperature calibration builds a similar table.
-
-    //#define BTC_SAMPLE_START  60.0f
-    //#define BTC_SAMPLE_RES    5.0f
-    //#define BTC_SAMPLE_COUNT  10U
-
-    // The temperature the probe should be at while taking measurements during bed temperature
-    // calibration.
-    //#define BTC_PROBE_TEMP 30.0f
-
-    // Height above Z=0.0f to raise the nozzle. Lowering this can help the probe to heat faster.
-    // Note: the Z=0.0f offset is determined by the probe offset which can be set using M851.
-    //#define PTC_PROBE_HEATING_OFFSET 0.5f
-
-    // Height to raise the Z-probe between heating and taking the next measurement. Some probes
-    // may fail to untrigger if they have been triggered for a long time, which can be solved by
-    // increasing the height the probe is raised to.
-    //#define PTC_PROBE_RAISE 15U
-
-    // If the probe is outside of the defined range, use linear extrapolation using the closest
-    // point and the PTC_LINEAR_EXTRAPOLATION'th next point. E.g. if set to 4 it will use data[0]
-    // and data[4] to perform linear extrapolation for values below PTC_SAMPLE_START.
-    //#define PTC_LINEAR_EXTRAPOLATION 4
+    // Height above Z=0.0 to raise the nozzle. Lowering this can help the probe to heat faster.
+    // Note: The Z=0.0 offset is determined by the probe Z offset (e.g., as set with M851 Z).
+    #define PTC_PROBE_HEATING_OFFSET 0.5
   #endif
-#endif
+#endif // PTC_PROBE || PTC_BED || PTC_HOTEND
 
 // @section extras
 
@@ -1791,20 +2295,23 @@
 //
 // G2/G3 Arc Support
 //
-#define ARC_SUPPORT                 // Disable this feature to save ~3226 bytes
+#define ARC_SUPPORT                   // Requires ~3226 bytes
 #if ENABLED(ARC_SUPPORT)
-  #define MM_PER_ARC_SEGMENT      1 // (mm) Length (or minimum length) of each arc segment
-  //#define ARC_SEGMENTS_PER_R    1 // Max segment length, MM_PER = Min
-  #define MIN_ARC_SEGMENTS       24 // Minimum number of segments in a complete circle
-  //#define ARC_SEGMENTS_PER_SEC 50 // Use feedrate to choose segment length (with MM_PER_ARC_SEGMENT as the minimum)
-  #define N_ARC_CORRECTION       25 // Number of interpolated segments between corrections
-  //#define ARC_P_CIRCLES           // Enable the 'P' parameter to specify complete circles
-  //#define CNC_WORKSPACE_PLANES    // Allow G2/G3 to operate in XY, ZX, or YZ planes
-  //#define SF_ARC_FIX              // Enable only if using SkeinForge with "Arc Point" fillet procedure
+  #define MIN_ARC_SEGMENT_MM      0.1 // (mm) Minimum length of each arc segment
+  #define MAX_ARC_SEGMENT_MM      1.0 // (mm) Maximum length of each arc segment
+  #define MIN_CIRCLE_SEGMENTS    72   // Minimum number of segments in a complete circle
+  //#define ARC_SEGMENTS_PER_SEC 50   // Use the feedrate to choose the segment length
+  #define N_ARC_CORRECTION       25   // Number of interpolated segments between corrections
+  //#define ARC_P_CIRCLES             // Enable the 'P' parameter to specify complete circles
+  //#define SF_ARC_FIX                // Enable only if using SkeinForge with "Arc Point" fillet procedure
 #endif
 
-// Support for G5 with XYZE destination and IJPQ offsets. Requires ~2666 bytes.
-//#define BEZIER_CURVE_SUPPORT
+// G5 Bézier Curve Support with XYZE destination and IJPQ offsets
+//#define BEZIER_CURVE_SUPPORT        // Requires ~2666 bytes
+
+#if EITHER(ARC_SUPPORT, BEZIER_CURVE_SUPPORT)
+  //#define CNC_WORKSPACE_PLANES      // Allow G2/G3/G5 to operate in XY, ZX, or YZ planes
+#endif
 
 /**
  * Direct Stepping
@@ -1887,7 +2394,7 @@
 // @section motion
 
 // The number of linear moves that can be in the planner at once.
-// The value of BLOCK_BUFFER_SIZE must be a power of 2 (e.g. 8, 16, 32)
+// The value of BLOCK_BUFFER_SIZE must be a power of 2 (e.g., 8, 16, 32)
 #if BOTH(SDSUPPORT, DIRECT_STEPPING)
   #define BLOCK_BUFFER_SIZE  8
 #elif ENABLED(SDSUPPORT)
@@ -1903,7 +2410,7 @@
 #define BUFSIZE 32
 
 // Transmission to Host Buffer Size
-// To save 386 bytes of PROGMEM (and TX_BUFFER_SIZE+3 bytes of RAM) set to 0.
+// To save 386 bytes of flash (and TX_BUFFER_SIZE+3 bytes of RAM) set to 0.
 // To buffer a simple "ok" you need 4 bytes.
 // For ADVANCED_OK (M105) you need 32 bytes.
 // For debug-echo: 128 bytes for the optimal speed.
@@ -1923,9 +2430,6 @@
   //#define SERIAL_XON_XOFF
 #endif
 
-// Add M575 G-code to change the baud rate
-//#define BAUD_RATE_GCODE
-
 #if ENABLED(SDSUPPORT)
   // Enable this option to collect and display the maximum
   // RX queue usage after transferring a file to SD.
@@ -1936,6 +2440,12 @@
   //#define SERIAL_STATS_DROPPED_RX
 #endif
 
+// Monitor RX buffer usage
+// Dump an error to the serial port if the serial receive buffer overflows.
+// If you see these errors, increase the RX_BUFFER_SIZE value.
+// Not supported on all platforms.
+//#define RX_BUFFER_MONITOR
+
 /**
  * Emergency Command Parser
  *
@@ -1946,6 +2456,26 @@
  */
 //#define EMERGENCY_PARSER
 
+/**
+ * Realtime Reporting (requires EMERGENCY_PARSER)
+ *
+ * - Report position and state of the machine (like Grbl).
+ * - Auto-report position during long moves.
+ * - Useful for CNC/LASER.
+ *
+ * Adds support for commands:
+ *  S000 : Report State and Position while moving.
+ *  P000 : Instant Pause / Hold while moving.
+ *  R000 : Resume from Pause / Hold.
+ *
+ * - During Hold all Emergency Parser commands are available, as usual.
+ * - Enable NANODLP_Z_SYNC and NANODLP_ALL_AXIS for move command end-state reports.
+ */
+//#define REALTIME_REPORTING_COMMANDS
+#if ENABLED(REALTIME_REPORTING_COMMANDS)
+  //#define FULL_REPORT_TO_HOST_FEATURE   // Auto-report the machine status like Grbl CNC
+#endif
+
 // Bad Serial-connections can miss a received command by sending an 'ok'
 // Therefore some clients abort after 30 seconds in a timeout.
 // Some other clients start sending commands while receiving a 'wait'.
@@ -1962,6 +2492,15 @@
 // For serial echo, the number of digits after the decimal point
 //#define SERIAL_FLOAT_PRECISION 4
 
+/**
+ * Set the number of proportional font spaces required to fill up a typical character space.
+ * This can help to better align the output of commands like `G29 O` Mesh Output.
+ *
+ * For clients that use a fixed-width font (like OctoPrint), leave this set to 1.0.
+ * Otherwise, adjust according to your client and font.
+ */
+#define PROPORTIONAL_FONT_RATIO 1.0
+
 // @section extras
 
 /**
@@ -1989,21 +2528,21 @@
  */
 //#define FWRETRACT
 #if ENABLED(FWRETRACT)
-  #define FWRETRACT_AUTORETRACT           // Override slicer retractions
+  #define FWRETRACT_AUTORETRACT             // Override slicer retractions
   #if ENABLED(FWRETRACT_AUTORETRACT)
-    #define MIN_AUTORETRACT 0.1           // (mm) Don't convert E moves under this length
-    #define MAX_AUTORETRACT 10.0          // (mm) Don't convert E moves over this length
+    #define MIN_AUTORETRACT             0.1 // (mm) Don't convert E moves under this length
+    #define MAX_AUTORETRACT            10.0 // (mm) Don't convert E moves over this length
   #endif
-  #define RETRACT_LENGTH 3                // (mm) Default retract length (positive value)
-  #define RETRACT_LENGTH_SWAP 13          // (mm) Default swap retract length (positive value)
-  #define RETRACT_FEEDRATE 45             // (mm/s) Default feedrate for retracting
-  #define RETRACT_ZRAISE 0                // (mm) Default retract Z-raise
-  #define RETRACT_RECOVER_LENGTH 0        // (mm) Default additional recover length (added to retract length on recover)
-  #define RETRACT_RECOVER_LENGTH_SWAP 0   // (mm) Default additional swap recover length (added to retract length on recover from toolchange)
-  #define RETRACT_RECOVER_FEEDRATE 8      // (mm/s) Default feedrate for recovering from retraction
-  #define RETRACT_RECOVER_FEEDRATE_SWAP 8 // (mm/s) Default feedrate for recovering from swap retraction
+  #define RETRACT_LENGTH                3   // (mm) Default retract length (positive value)
+  #define RETRACT_LENGTH_SWAP          13   // (mm) Default swap retract length (positive value)
+  #define RETRACT_FEEDRATE             45   // (mm/s) Default feedrate for retracting
+  #define RETRACT_ZRAISE                0   // (mm) Default retract Z-raise
+  #define RETRACT_RECOVER_LENGTH        0   // (mm) Default additional recover length (added to retract length on recover)
+  #define RETRACT_RECOVER_LENGTH_SWAP   0   // (mm) Default additional swap recover length (added to retract length on recover from toolchange)
+  #define RETRACT_RECOVER_FEEDRATE      8   // (mm/s) Default feedrate for recovering from retraction
+  #define RETRACT_RECOVER_FEEDRATE_SWAP 8   // (mm/s) Default feedrate for recovering from swap retraction
   #if ENABLED(MIXING_EXTRUDER)
-    //#define RETRACT_SYNC_MIXING         // Retract and restore all mixing steppers simultaneously
+    //#define RETRACT_SYNC_MIXING           // Retract and restore all mixing steppers simultaneously
   #endif
 #endif
 
@@ -2020,6 +2559,20 @@
     //#define EVENT_GCODE_AFTER_TOOLCHANGE "G12X"   // Extra G-code to run after tool-change
   #endif
 
+  /**
+   * Extra G-code to run while executing tool-change commands. Can be used to use an additional
+   * stepper motor (e.g., I axis in Configuration.h) to drive the tool-changer.
+   */
+  //#define EVENT_GCODE_TOOLCHANGE_T0 "G28 A\nG1 A0" // Extra G-code to run while executing tool-change command T0
+  //#define EVENT_GCODE_TOOLCHANGE_T1 "G1 A10"       // Extra G-code to run while executing tool-change command T1
+  //#define EVENT_GCODE_TOOLCHANGE_ALWAYS_RUN        // Always execute above G-code sequences. Use with caution!
+
+  /**
+   * Tool Sensors detect when tools have been picked up or dropped.
+   * Requires the pins TOOL_SENSOR1_PIN, TOOL_SENSOR2_PIN, etc.
+   */
+  //#define TOOL_SENSOR
+
   /**
    * Retract and prime filament on tool-change to reduce
    * ooze and stringing and to get cleaner transitions.
@@ -2028,26 +2581,30 @@
   #if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
     // Load / Unload
     #define TOOLCHANGE_FS_LENGTH              12  // (mm) Load / Unload length
-    #define TOOLCHANGE_FS_EXTRA_RESUME_LENGTH  0  // (mm) Extra length for better restart, fine tune by LCD/Gcode)
+    #define TOOLCHANGE_FS_EXTRA_RESUME_LENGTH  0  // (mm) Extra length for better restart. Adjust with LCD or M217 B.
     #define TOOLCHANGE_FS_RETRACT_SPEED   (50*60) // (mm/min) (Unloading)
     #define TOOLCHANGE_FS_UNRETRACT_SPEED (25*60) // (mm/min) (On SINGLENOZZLE or Bowden loading must be slowed down)
 
     // Longer prime to clean out a SINGLENOZZLE
     #define TOOLCHANGE_FS_EXTRA_PRIME          0  // (mm) Extra priming length
     #define TOOLCHANGE_FS_PRIME_SPEED    (4.6*60) // (mm/min) Extra priming feedrate
-    #define TOOLCHANGE_FS_WIPE_RETRACT         0  // (mm/min) Retract before cooling for less stringing, better wipe, etc.
+    #define TOOLCHANGE_FS_WIPE_RETRACT         0  // (mm) Cutting retraction out of park, for less stringing, better wipe, etc. Adjust with LCD or M217 G.
 
     // Cool after prime to reduce stringing
     #define TOOLCHANGE_FS_FAN                 -1  // Fan index or -1 to skip
     #define TOOLCHANGE_FS_FAN_SPEED          255  // 0-255
     #define TOOLCHANGE_FS_FAN_TIME            10  // (seconds)
 
-    // Swap uninitialized extruder with TOOLCHANGE_FS_PRIME_SPEED for all lengths (recover + prime)
-    // (May break filament if not retracted beforehand.)
-    //#define TOOLCHANGE_FS_INIT_BEFORE_SWAP
+    // Use TOOLCHANGE_FS_PRIME_SPEED feedrate the first time each extruder is primed
+    //#define TOOLCHANGE_FS_SLOW_FIRST_PRIME
 
-    // Prime on the first T0 (If other, TOOLCHANGE_FS_INIT_BEFORE_SWAP applied)
-    // Enable it (M217 V[0/1]) before printing, to avoid unwanted priming on host connect
+    /**
+     * Prime T0 the first time T0 is sent to the printer:
+     *  [ Power-On -> T0 { Activate & Prime T0 } -> T1 { Retract T0, Activate & Prime T1 } ]
+     * If disabled, no priming on T0 until switching back to T0 from another extruder:
+     *  [ Power-On -> T0 { T0 Activated } -> T1 { Activate & Prime T1 } -> T0 { Retract T1, Activate & Prime T0 } ]
+     * Enable with M217 V1 before printing to avoid unwanted priming on host connect.
+     */
     //#define TOOLCHANGE_FS_PRIME_FIRST_USED
 
     /**
@@ -2077,15 +2634,18 @@
   #endif
 #endif // HAS_MULTI_EXTRUDER
 
+// @section advanced pause
+
 /**
- * Advanced Pause
- * Experimental feature for filament change support and for parking the nozzle when paused.
- * Adds the GCode M600 for initiating filament change.
- * If PARK_HEAD_ON_PAUSE enabled, adds the GCode M125 to pause printing and park the nozzle.
+ * Advanced Pause for Filament Change
+ *  - Adds the G-code M600 Filament Change to initiate a filament change.
+ *  - This feature is required for the default FILAMENT_RUNOUT_SCRIPT.
  *
- * Requires an LCD display.
- * Requires NOZZLE_PARK_FEATURE.
- * This feature is required for the default FILAMENT_RUNOUT_SCRIPT.
+ * Requirements:
+ *  - For Filament Change parking enable and configure NOZZLE_PARK_FEATURE.
+ *  - For user interaction enable an LCD display, HOST_PROMPT_SUPPORT, or EMERGENCY_PARSER.
+ *
+ * Enable PARK_HEAD_ON_PAUSE to add the G-code M125 Pause and Park.
  */
 //#define ADVANCED_PAUSE_FEATURE
 #if ENABLED(ADVANCED_PAUSE_FEATURE)
@@ -2124,6 +2684,8 @@
   #define PAUSE_PARK_NOZZLE_TIMEOUT           45  // (seconds) Time limit before the nozzle is turned off for safety.
   #define FILAMENT_CHANGE_ALERT_BEEPS         10  // Number of alert beeps to play when a response is needed.
   #define PAUSE_PARK_NO_STEPPER_TIMEOUT           // Enable for XYZ steppers to stay powered on during filament change.
+  //#define FILAMENT_CHANGE_RESUME_ON_INSERT      // Automatically continue / load filament when runout sensor is triggered again.
+  //#define PAUSE_REHEAT_FAST_RESUME              // Reduce number of waits by not prompting again post-timeout before continuing.
 
   //#define PARK_HEAD_ON_PAUSE                    // Park the nozzle during pause and filament change.
   //#define HOME_BEFORE_FILAMENT_CHANGE           // If needed, home before parking for filament change
@@ -2132,257 +2694,256 @@
   //#define FILAMENT_UNLOAD_ALL_EXTRUDERS         // Allow M702 to unload all extruders above a minimum target temp (as set by M302)
 #endif
 
-// @section tmc
-
-/**
- * TMC26X Stepper Driver options
- *
- * The TMC26XStepper library is required for this stepper driver.
- * https://github.com/trinamic/TMC26XStepper
- */
-#if HAS_DRIVER(TMC26X)
-
-  #if AXIS_DRIVER_TYPE_X(TMC26X)
-    #define X_MAX_CURRENT     1000  // (mA)
-    #define X_SENSE_RESISTOR    91  // (mOhms)
-    #define X_MICROSTEPS        16  // Number of microsteps
-  #endif
-
-  #if AXIS_DRIVER_TYPE_X2(TMC26X)
-    #define X2_MAX_CURRENT    1000
-    #define X2_SENSE_RESISTOR   91
-    #define X2_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_Y(TMC26X)
-    #define Y_MAX_CURRENT     1000
-    #define Y_SENSE_RESISTOR    91
-    #define Y_MICROSTEPS        16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_Y2(TMC26X)
-    #define Y2_MAX_CURRENT    1000
-    #define Y2_SENSE_RESISTOR   91
-    #define Y2_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_Z(TMC26X)
-    #define Z_MAX_CURRENT     1000
-    #define Z_SENSE_RESISTOR    91
-    #define Z_MICROSTEPS        16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_Z2(TMC26X)
-    #define Z2_MAX_CURRENT    1000
-    #define Z2_SENSE_RESISTOR   91
-    #define Z2_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_Z3(TMC26X)
-    #define Z3_MAX_CURRENT    1000
-    #define Z3_SENSE_RESISTOR   91
-    #define Z3_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_Z4(TMC26X)
-    #define Z4_MAX_CURRENT    1000
-    #define Z4_SENSE_RESISTOR   91
-    #define Z4_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_E0(TMC26X)
-    #define E0_MAX_CURRENT    1000
-    #define E0_SENSE_RESISTOR   91
-    #define E0_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_E1(TMC26X)
-    #define E1_MAX_CURRENT    1000
-    #define E1_SENSE_RESISTOR   91
-    #define E1_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_E2(TMC26X)
-    #define E2_MAX_CURRENT    1000
-    #define E2_SENSE_RESISTOR   91
-    #define E2_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_E3(TMC26X)
-    #define E3_MAX_CURRENT    1000
-    #define E3_SENSE_RESISTOR   91
-    #define E3_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_E4(TMC26X)
-    #define E4_MAX_CURRENT    1000
-    #define E4_SENSE_RESISTOR   91
-    #define E4_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_E5(TMC26X)
-    #define E5_MAX_CURRENT    1000
-    #define E5_SENSE_RESISTOR   91
-    #define E5_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_E6(TMC26X)
-    #define E6_MAX_CURRENT    1000
-    #define E6_SENSE_RESISTOR   91
-    #define E6_MICROSTEPS       16
-  #endif
-
-  #if AXIS_DRIVER_TYPE_E7(TMC26X)
-    #define E7_MAX_CURRENT    1000
-    #define E7_SENSE_RESISTOR   91
-    #define E7_MICROSTEPS       16
-  #endif
-
-#endif // TMC26X
-
 // @section tmc_smart
 
 /**
- * To use TMC2130, TMC2160, TMC2660, TMC5130, TMC5160 stepper drivers in SPI mode
- * connect your SPI pins to the hardware SPI interface on your board and define
- * the required CS pins in your `pins_MYBOARD.h` file. (e.g., RAMPS 1.4 uses AUX3
- * pins `X_CS_PIN 53`, `Y_CS_PIN 49`, etc.).
- * You may also use software SPI if you wish to use general purpose IO pins.
+ * Trinamic Smart Drivers
  *
- * To use TMC2208 stepper UART-configurable stepper drivers connect #_SERIAL_TX_PIN
- * to the driver side PDN_UART pin with a 1K resistor.
- * To use the reading capabilities, also connect #_SERIAL_RX_PIN to PDN_UART without
- * a resistor.
- * The drivers can also be used with hardware serial.
+ * To use TMC2130, TMC2160, TMC2660, TMC5130, TMC5160 stepper drivers in SPI mode:
+ *  - Connect your SPI pins to the Hardware SPI interface on the board.
+ *    Some boards have simple jumper connections! See your board's documentation.
+ *  - Define the required Stepper CS pins in your `pins_MYBOARD.h` file.
+ *    (See the RAMPS pins, for example.)
+ *  - You can also use Software SPI with GPIO pins instead of Hardware SPI.
  *
- * TMCStepper library is required to use TMC stepper drivers.
- * https://github.com/teemuatlut/TMCStepper
+ * To use TMC220x stepper drivers with Serial UART:
+ *  - Connect PDN_UART to the #_SERIAL_TX_PIN through a 1K resistor.
+ *    For reading capabilities also connect PDN_UART to #_SERIAL_RX_PIN with no resistor.
+ *    Some boards have simple jumper connections! See your board's documentation.
+ *  - These drivers can also be used with Hardware Serial.
+ *
+ * The TMC26XStepper library is required for TMC26X stepper drivers.
+ *   https://github.com/MarlinFirmware/TMC26XStepper
+ *
+ * The TMCStepper library is required for other TMC stepper drivers.
+ *   https://github.com/teemuatlut/TMCStepper
+ *
+ * @section tmc/config
  */
-#if HAS_TRINAMIC_CONFIG
+#if HAS_TRINAMIC_CONFIG || HAS_TMC26X
 
   #define HOLD_MULTIPLIER    0.5  // Scales down the holding current from run current
-  #define INTERPOLATE       true  // Interpolate X/Y/Z_MICROSTEPS to 256
 
-  #if AXIS_IS_TMC(X)
+  /**
+   * Interpolate microsteps to 256
+   * Override for each driver with _INTERPOLATE settings below
+   */
+  #define INTERPOLATE      true
+
+  #if AXIS_IS_TMC_CONFIG(X)
     #define X_CURRENT       800        // (mA) RMS current. Multiply by 1.414 for peak current.
     #define X_CURRENT_HOME  X_CURRENT  // (mA) RMS current for sensorless homing
-    #define X_MICROSTEPS     16    // 0..256
-    #define X_RSENSE          0.11
-    #define X_CHAIN_POS      -1    // <=0 : Not chained. 1 : MCU MOSI connected. 2 : Next in chain, ...
+    #define X_MICROSTEPS     16        // 0..256
+    #define X_RSENSE          0.11     // Multiplied x1000 for TMC26X
+    #define X_CHAIN_POS      -1        // -1..0: Not chained. 1: MCU MOSI connected. 2: Next in chain, ...
+    //#define X_INTERPOLATE  true      // Enable to override 'INTERPOLATE' for the X axis
+    //#define X_HOLD_MULTIPLIER 0.5    // Enable to override 'HOLD_MULTIPLIER' for the X axis
   #endif
 
-  #if AXIS_IS_TMC(X2)
+  #if AXIS_IS_TMC_CONFIG(X2)
     #define X2_CURRENT      800
     #define X2_CURRENT_HOME X2_CURRENT
-    #define X2_MICROSTEPS    16
+    #define X2_MICROSTEPS    X_MICROSTEPS
     #define X2_RSENSE         0.11
     #define X2_CHAIN_POS     -1
+    //#define X2_INTERPOLATE true
+    //#define X2_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(Y)
+  #if AXIS_IS_TMC_CONFIG(Y)
     #define Y_CURRENT       800
     #define Y_CURRENT_HOME  Y_CURRENT
     #define Y_MICROSTEPS     16
     #define Y_RSENSE          0.11
     #define Y_CHAIN_POS      -1
+    //#define Y_INTERPOLATE  true
+    //#define Y_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(Y2)
+  #if AXIS_IS_TMC_CONFIG(Y2)
     #define Y2_CURRENT      800
     #define Y2_CURRENT_HOME Y2_CURRENT
-    #define Y2_MICROSTEPS    16
+    #define Y2_MICROSTEPS    Y_MICROSTEPS
     #define Y2_RSENSE         0.11
     #define Y2_CHAIN_POS     -1
+    //#define Y2_INTERPOLATE true
+    //#define Y2_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(Z)
+  #if AXIS_IS_TMC_CONFIG(Z)
     #define Z_CURRENT       800
     #define Z_CURRENT_HOME  Z_CURRENT
     #define Z_MICROSTEPS     16
     #define Z_RSENSE          0.11
     #define Z_CHAIN_POS      -1
+    //#define Z_INTERPOLATE  true
+    //#define Z_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(Z2)
+  #if AXIS_IS_TMC_CONFIG(Z2)
     #define Z2_CURRENT      800
     #define Z2_CURRENT_HOME Z2_CURRENT
-    #define Z2_MICROSTEPS    16
+    #define Z2_MICROSTEPS    Z_MICROSTEPS
     #define Z2_RSENSE         0.11
     #define Z2_CHAIN_POS     -1
+    //#define Z2_INTERPOLATE true
+    //#define Z2_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(Z3)
+  #if AXIS_IS_TMC_CONFIG(Z3)
     #define Z3_CURRENT      800
     #define Z3_CURRENT_HOME Z3_CURRENT
-    #define Z3_MICROSTEPS    16
+    #define Z3_MICROSTEPS    Z_MICROSTEPS
     #define Z3_RSENSE         0.11
     #define Z3_CHAIN_POS     -1
+    //#define Z3_INTERPOLATE true
+    //#define Z3_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(Z4)
+  #if AXIS_IS_TMC_CONFIG(Z4)
     #define Z4_CURRENT      800
     #define Z4_CURRENT_HOME Z4_CURRENT
-    #define Z4_MICROSTEPS    16
+    #define Z4_MICROSTEPS    Z_MICROSTEPS
     #define Z4_RSENSE         0.11
     #define Z4_CHAIN_POS     -1
+    //#define Z4_INTERPOLATE true
+    //#define Z4_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(E0)
+  #if AXIS_IS_TMC_CONFIG(I)
+    #define I_CURRENT      800
+    #define I_CURRENT_HOME I_CURRENT
+    #define I_MICROSTEPS    16
+    #define I_RSENSE         0.11
+    #define I_CHAIN_POS     -1
+    //#define I_INTERPOLATE  true
+    //#define I_HOLD_MULTIPLIER 0.5
+  #endif
+
+  #if AXIS_IS_TMC_CONFIG(J)
+    #define J_CURRENT      800
+    #define J_CURRENT_HOME J_CURRENT
+    #define J_MICROSTEPS    16
+    #define J_RSENSE         0.11
+    #define J_CHAIN_POS     -1
+    //#define J_INTERPOLATE  true
+    //#define J_HOLD_MULTIPLIER 0.5
+  #endif
+
+  #if AXIS_IS_TMC_CONFIG(K)
+    #define K_CURRENT      800
+    #define K_CURRENT_HOME K_CURRENT
+    #define K_MICROSTEPS    16
+    #define K_RSENSE         0.11
+    #define K_CHAIN_POS     -1
+    //#define K_INTERPOLATE  true
+    //#define K_HOLD_MULTIPLIER 0.5
+  #endif
+
+  #if AXIS_IS_TMC_CONFIG(U)
+    #define U_CURRENT      800
+    #define U_CURRENT_HOME U_CURRENT
+    #define U_MICROSTEPS     8
+    #define U_RSENSE         0.11
+    #define U_CHAIN_POS     -1
+    //#define U_INTERPOLATE  true
+    //#define U_HOLD_MULTIPLIER 0.5
+  #endif
+
+  #if AXIS_IS_TMC_CONFIG(V)
+    #define V_CURRENT      800
+    #define V_CURRENT_HOME V_CURRENT
+    #define V_MICROSTEPS     8
+    #define V_RSENSE         0.11
+    #define V_CHAIN_POS     -1
+    //#define V_INTERPOLATE  true
+    //#define V_HOLD_MULTIPLIER 0.5
+  #endif
+
+  #if AXIS_IS_TMC_CONFIG(W)
+    #define W_CURRENT      800
+    #define W_CURRENT_HOME W_CURRENT
+    #define W_MICROSTEPS     8
+    #define W_RSENSE         0.11
+    #define W_CHAIN_POS     -1
+    //#define W_INTERPOLATE  true
+    //#define W_HOLD_MULTIPLIER 0.5
+  #endif
+
+  #if AXIS_IS_TMC_CONFIG(E0)
     #define E0_CURRENT      800
     #define E0_MICROSTEPS    16
     #define E0_RSENSE         0.11
     #define E0_CHAIN_POS     -1
+    //#define E0_INTERPOLATE true
+    //#define E0_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(E1)
+  #if AXIS_IS_TMC_CONFIG(E1)
     #define E1_CURRENT      800
-    #define E1_MICROSTEPS    16
+    #define E1_MICROSTEPS   E0_MICROSTEPS
     #define E1_RSENSE         0.11
     #define E1_CHAIN_POS     -1
+    //#define E1_INTERPOLATE true
+    //#define E1_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(E2)
+  #if AXIS_IS_TMC_CONFIG(E2)
     #define E2_CURRENT      800
-    #define E2_MICROSTEPS    16
+    #define E2_MICROSTEPS   E0_MICROSTEPS
     #define E2_RSENSE         0.11
     #define E2_CHAIN_POS     -1
+    //#define E2_INTERPOLATE true
+    //#define E2_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(E3)
+  #if AXIS_IS_TMC_CONFIG(E3)
     #define E3_CURRENT      800
-    #define E3_MICROSTEPS    16
+    #define E3_MICROSTEPS   E0_MICROSTEPS
     #define E3_RSENSE         0.11
     #define E3_CHAIN_POS     -1
+    //#define E3_INTERPOLATE true
+    //#define E3_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(E4)
+  #if AXIS_IS_TMC_CONFIG(E4)
     #define E4_CURRENT      800
-    #define E4_MICROSTEPS    16
+    #define E4_MICROSTEPS   E0_MICROSTEPS
     #define E4_RSENSE         0.11
     #define E4_CHAIN_POS     -1
+    //#define E4_INTERPOLATE true
+    //#define E4_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(E5)
+  #if AXIS_IS_TMC_CONFIG(E5)
     #define E5_CURRENT      800
-    #define E5_MICROSTEPS    16
+    #define E5_MICROSTEPS   E0_MICROSTEPS
     #define E5_RSENSE         0.11
     #define E5_CHAIN_POS     -1
+    //#define E5_INTERPOLATE true
+    //#define E5_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(E6)
+  #if AXIS_IS_TMC_CONFIG(E6)
     #define E6_CURRENT      800
-    #define E6_MICROSTEPS    16
+    #define E6_MICROSTEPS   E0_MICROSTEPS
     #define E6_RSENSE         0.11
     #define E6_CHAIN_POS     -1
+    //#define E6_INTERPOLATE true
+    //#define E6_HOLD_MULTIPLIER 0.5
   #endif
 
-  #if AXIS_IS_TMC(E7)
+  #if AXIS_IS_TMC_CONFIG(E7)
     #define E7_CURRENT      800
-    #define E7_MICROSTEPS    16
+    #define E7_MICROSTEPS   E0_MICROSTEPS
     #define E7_RSENSE         0.11
     #define E7_CHAIN_POS     -1
+    //#define E7_INTERPOLATE true
+    //#define E7_HOLD_MULTIPLIER 0.5
   #endif
 
+  // @section tmc/spi
+
   /**
    * Override default SPI pins for TMC2130, TMC2160, TMC2660, TMC5130 and TMC5160 drivers here.
    * The default pins can be found in your board's pins file.
@@ -2394,6 +2955,13 @@
   //#define Y2_CS_PIN         -1
   //#define Z2_CS_PIN         -1
   //#define Z3_CS_PIN         -1
+  //#define Z4_CS_PIN         -1
+  //#define I_CS_PIN          -1
+  //#define J_CS_PIN          -1
+  //#define K_CS_PIN          -1
+  //#define U_CS_PIN          -1
+  //#define V_CS_PIN          -1
+  //#define W_CS_PIN          -1
   //#define E0_CS_PIN         -1
   //#define E1_CS_PIN         -1
   //#define E2_CS_PIN         -1
@@ -2413,6 +2981,8 @@
   //#define TMC_SW_MISO       -1
   //#define TMC_SW_SCK        -1
 
+  // @section tmc/serial
+
   /**
    * Four TMC2209 drivers can use the same HW/SW serial port with hardware configured addresses.
    * Set the address using jumpers on pins MS1 and MS2.
@@ -2425,22 +2995,30 @@
    * Set *_SERIAL_TX_PIN and *_SERIAL_RX_PIN to match for all drivers
    * on the same serial port, either here or in your board's pins file.
    */
-  #define  X_SLAVE_ADDRESS 0
-  #define  Y_SLAVE_ADDRESS 0
-  #define  Z_SLAVE_ADDRESS 0
-  #define X2_SLAVE_ADDRESS 0
-  #define Y2_SLAVE_ADDRESS 0
-  #define Z2_SLAVE_ADDRESS 0
-  #define Z3_SLAVE_ADDRESS 0
-  #define Z4_SLAVE_ADDRESS 0
-  #define E0_SLAVE_ADDRESS 0
-  #define E1_SLAVE_ADDRESS 0
-  #define E2_SLAVE_ADDRESS 0
-  #define E3_SLAVE_ADDRESS 0
-  #define E4_SLAVE_ADDRESS 0
-  #define E5_SLAVE_ADDRESS 0
-  #define E6_SLAVE_ADDRESS 0
-  #define E7_SLAVE_ADDRESS 0
+  //#define  X_SLAVE_ADDRESS 0
+  //#define  Y_SLAVE_ADDRESS 0
+  //#define  Z_SLAVE_ADDRESS 0
+  //#define X2_SLAVE_ADDRESS 0
+  //#define Y2_SLAVE_ADDRESS 0
+  //#define Z2_SLAVE_ADDRESS 0
+  //#define Z3_SLAVE_ADDRESS 0
+  //#define Z4_SLAVE_ADDRESS 0
+  //#define  I_SLAVE_ADDRESS 0
+  //#define  J_SLAVE_ADDRESS 0
+  //#define  K_SLAVE_ADDRESS 0
+  //#define  U_SLAVE_ADDRESS 0
+  //#define  V_SLAVE_ADDRESS 0
+  //#define  W_SLAVE_ADDRESS 0
+  //#define E0_SLAVE_ADDRESS 0
+  //#define E1_SLAVE_ADDRESS 0
+  //#define E2_SLAVE_ADDRESS 0
+  //#define E3_SLAVE_ADDRESS 0
+  //#define E4_SLAVE_ADDRESS 0
+  //#define E5_SLAVE_ADDRESS 0
+  //#define E6_SLAVE_ADDRESS 0
+  //#define E7_SLAVE_ADDRESS 0
+
+  // @section tmc/smart
 
   /**
    * Software enable
@@ -2450,14 +3028,24 @@
    */
   //#define SOFTWARE_DRIVER_ENABLE
 
+  // @section tmc/stealthchop
+
   /**
    * TMC2130, TMC2160, TMC2208, TMC2209, TMC5130 and TMC5160 only
    * Use Trinamic's ultra quiet stepping mode.
    * When disabled, Marlin will use spreadCycle stepping mode.
    */
-  #define STEALTHCHOP_XY
-  #define STEALTHCHOP_Z
-  #define STEALTHCHOP_E
+  #if HAS_STEALTHCHOP
+    #define STEALTHCHOP_XY
+    #define STEALTHCHOP_Z
+    #define STEALTHCHOP_I
+    #define STEALTHCHOP_J
+    #define STEALTHCHOP_K
+    #define STEALTHCHOP_U
+    #define STEALTHCHOP_V
+    #define STEALTHCHOP_W
+    #define STEALTHCHOP_E
+  #endif
 
   /**
    * Optimize spreadCycle chopper parameters by using predefined parameter sets
@@ -2471,10 +3059,34 @@
    * CHOPPER_PRUSAMK3_24V // Imported parameters from the official Průša firmware for MK3 (24V)
    * CHOPPER_MARLIN_119   // Old defaults from Marlin v1.1.9
    *
-   * Define you own with
+   * Define your own with:
    * { , , hysteresis_start[1..8] }
    */
-  #define CHOPPER_TIMING CHOPPER_DEFAULT_12V
+  #define CHOPPER_TIMING CHOPPER_DEFAULT_12V        // All axes (override below)
+  //#define CHOPPER_TIMING_X  CHOPPER_TIMING        // For X Axes (override below)
+  //#define CHOPPER_TIMING_X2 CHOPPER_TIMING_X
+  //#define CHOPPER_TIMING_Y  CHOPPER_TIMING        // For Y Axes (override below)
+  //#define CHOPPER_TIMING_Y2 CHOPPER_TIMING_Y
+  //#define CHOPPER_TIMING_Z  CHOPPER_TIMING        // For Z Axes (override below)
+  //#define CHOPPER_TIMING_Z2 CHOPPER_TIMING_Z
+  //#define CHOPPER_TIMING_Z3 CHOPPER_TIMING_Z
+  //#define CHOPPER_TIMING_Z4 CHOPPER_TIMING_Z
+  //#define CHOPPER_TIMING_I  CHOPPER_TIMING        // For I Axis
+  //#define CHOPPER_TIMING_J  CHOPPER_TIMING        // For J Axis
+  //#define CHOPPER_TIMING_K  CHOPPER_TIMING        // For K Axis
+  //#define CHOPPER_TIMING_U  CHOPPER_TIMING        // For U Axis
+  //#define CHOPPER_TIMING_V  CHOPPER_TIMING        // For V Axis
+  //#define CHOPPER_TIMING_W  CHOPPER_TIMING        // For W Axis
+  //#define CHOPPER_TIMING_E  CHOPPER_TIMING        // For Extruders (override below)
+  //#define CHOPPER_TIMING_E1 CHOPPER_TIMING_E
+  //#define CHOPPER_TIMING_E2 CHOPPER_TIMING_E
+  //#define CHOPPER_TIMING_E3 CHOPPER_TIMING_E
+  //#define CHOPPER_TIMING_E4 CHOPPER_TIMING_E
+  //#define CHOPPER_TIMING_E5 CHOPPER_TIMING_E
+  //#define CHOPPER_TIMING_E6 CHOPPER_TIMING_E
+  //#define CHOPPER_TIMING_E7 CHOPPER_TIMING_E
+
+  // @section tmc/status
 
   /**
    * Monitor Trinamic drivers
@@ -2495,6 +3107,8 @@
     #define STOP_ON_ERROR
   #endif
 
+  // @section tmc/hybrid
+
   /**
    * TMC2130, TMC2160, TMC2208, TMC2209, TMC5130 and TMC5160 only
    * The driver will switch to spreadCycle when stepper speed is over HYBRID_THRESHOLD.
@@ -2512,6 +3126,12 @@
   #define Z2_HYBRID_THRESHOLD      3
   #define Z3_HYBRID_THRESHOLD      3
   #define Z4_HYBRID_THRESHOLD      3
+  #define I_HYBRID_THRESHOLD       3  // [linear=mm/s, rotational=°/s]
+  #define J_HYBRID_THRESHOLD       3  // [linear=mm/s, rotational=°/s]
+  #define K_HYBRID_THRESHOLD       3  // [linear=mm/s, rotational=°/s]
+  #define U_HYBRID_THRESHOLD       3  // [mm/s]
+  #define V_HYBRID_THRESHOLD       3
+  #define W_HYBRID_THRESHOLD       3
   #define E0_HYBRID_THRESHOLD     20
   #define E1_HYBRID_THRESHOLD     30
   #define E2_HYBRID_THRESHOLD     30
@@ -2537,7 +3157,7 @@
    *
    * It is recommended to set HOMING_BUMP_MM to { 0, 0, 0 }.
    *
-   * SPI_ENDSTOPS  *** Beta feature! *** TMC2130 Only ***
+   * SPI_ENDSTOPS  *** Beta feature! *** TMC2130/TMC5160 Only ***
    * Poll the driver through SPI to determine load when homing.
    * Removes the need for a wire from DIAG1 to an endstop pin.
    *
@@ -2545,6 +3165,7 @@
    * homing and adds a guard period for endstop triggering.
    *
    * Comment *_STALL_SENSITIVITY to disable sensorless homing for that axis.
+   * @section tmc/stallguard
    */
   #define SENSORLESS_HOMING // StallGuard capable drivers only
 
@@ -2558,10 +3179,18 @@
     //#define Z2_STALL_SENSITIVITY Z_STALL_SENSITIVITY
     //#define Z3_STALL_SENSITIVITY Z_STALL_SENSITIVITY
     //#define Z4_STALL_SENSITIVITY Z_STALL_SENSITIVITY
+    //#define I_STALL_SENSITIVITY  8
+    //#define J_STALL_SENSITIVITY  8
+    //#define K_STALL_SENSITIVITY  8
+    //#define U_STALL_SENSITIVITY  8
+    //#define V_STALL_SENSITIVITY  8
+    //#define W_STALL_SENSITIVITY  8
     //#define SPI_ENDSTOPS              // TMC2130 only
     #define IMPROVE_HOMING_RELIABILITY
   #endif
 
+  // @section tmc/config
+
   /**
    * TMC Homing stepper phase.
    *
@@ -2582,7 +3211,7 @@
 
   /**
    * Enable M122 debugging command for TMC stepper drivers.
-   * M122 S0/1 will enable continous reporting.
+   * M122 S0/1 will enable continuous reporting.
    */
   #define TMC_DEBUG
 
@@ -2599,201 +3228,7 @@
    */
   #define TMC_ADV() {  }
 
-#endif // HAS_TRINAMIC_CONFIG
-
-// @section L64XX
-
-/**
- * L64XX Stepper Driver options
- *
- * Arduino-L6470 library (0.8.0 or higher) is required.
- * https://github.com/ameyer/Arduino-L6470
- *
- * Requires the following to be defined in your pins_YOUR_BOARD file
- *     L6470_CHAIN_SCK_PIN
- *     L6470_CHAIN_MISO_PIN
- *     L6470_CHAIN_MOSI_PIN
- *     L6470_CHAIN_SS_PIN
- *     ENABLE_RESET_L64XX_CHIPS(Q)  where Q is 1 to enable and 0 to reset
- */
-
-#if HAS_L64XX
-
-  //#define L6470_CHITCHAT        // Display additional status info
-
-  #if AXIS_IS_L64XX(X)
-    #define X_MICROSTEPS       128  // Number of microsteps (VALID: 1, 2, 4, 8, 16, 32, 128) - L6474 max is 16
-    #define X_OVERCURRENT     2000  // (mA) Current where the driver detects an over current
-                                    //   L6470 & L6474 - VALID: 375 x (1 - 16) - 6A max - rounds down
-                                    //   POWERSTEP01: VALID: 1000 x (1 - 32) - 32A max - rounds down
-    #define X_STALLCURRENT    1500  // (mA) Current where the driver detects a stall (VALID: 31.25 * (1-128) -  4A max - rounds down)
-                                    //   L6470 & L6474 - VALID: 31.25 * (1-128) -  4A max - rounds down
-                                    //   POWERSTEP01: VALID: 200 x (1 - 32) - 6.4A max - rounds down
-                                    //   L6474 - STALLCURRENT setting is used to set the nominal (TVAL) current
-    #define X_MAX_VOLTAGE      127  // 0-255, Maximum effective voltage seen by stepper - not used by L6474
-    #define X_CHAIN_POS         -1  // Position in SPI chain, 0=Not in chain, 1=Nearest MOSI
-    #define X_SLEW_RATE          1  // 0-3, Slew 0 is slowest, 3 is fastest
-  #endif
-
-  #if AXIS_IS_L64XX(X2)
-    #define X2_MICROSTEPS      128
-    #define X2_OVERCURRENT    2000
-    #define X2_STALLCURRENT   1500
-    #define X2_MAX_VOLTAGE     127
-    #define X2_CHAIN_POS        -1
-    #define X2_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(Y)
-    #define Y_MICROSTEPS       128
-    #define Y_OVERCURRENT     2000
-    #define Y_STALLCURRENT    1500
-    #define Y_MAX_VOLTAGE      127
-    #define Y_CHAIN_POS         -1
-    #define Y_SLEW_RATE          1
-  #endif
-
-  #if AXIS_IS_L64XX(Y2)
-    #define Y2_MICROSTEPS      128
-    #define Y2_OVERCURRENT    2000
-    #define Y2_STALLCURRENT   1500
-    #define Y2_MAX_VOLTAGE     127
-    #define Y2_CHAIN_POS        -1
-    #define Y2_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(Z)
-    #define Z_MICROSTEPS       128
-    #define Z_OVERCURRENT     2000
-    #define Z_STALLCURRENT    1500
-    #define Z_MAX_VOLTAGE      127
-    #define Z_CHAIN_POS         -1
-    #define Z_SLEW_RATE          1
-  #endif
-
-  #if AXIS_IS_L64XX(Z2)
-    #define Z2_MICROSTEPS      128
-    #define Z2_OVERCURRENT    2000
-    #define Z2_STALLCURRENT   1500
-    #define Z2_MAX_VOLTAGE     127
-    #define Z2_CHAIN_POS        -1
-    #define Z2_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(Z3)
-    #define Z3_MICROSTEPS      128
-    #define Z3_OVERCURRENT    2000
-    #define Z3_STALLCURRENT   1500
-    #define Z3_MAX_VOLTAGE     127
-    #define Z3_CHAIN_POS        -1
-    #define Z3_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(Z4)
-    #define Z4_MICROSTEPS      128
-    #define Z4_OVERCURRENT    2000
-    #define Z4_STALLCURRENT   1500
-    #define Z4_MAX_VOLTAGE     127
-    #define Z4_CHAIN_POS        -1
-    #define Z4_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(E0)
-    #define E0_MICROSTEPS      128
-    #define E0_OVERCURRENT    2000
-    #define E0_STALLCURRENT   1500
-    #define E0_MAX_VOLTAGE     127
-    #define E0_CHAIN_POS        -1
-    #define E0_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(E1)
-    #define E1_MICROSTEPS      128
-    #define E1_OVERCURRENT    2000
-    #define E1_STALLCURRENT   1500
-    #define E1_MAX_VOLTAGE     127
-    #define E1_CHAIN_POS        -1
-    #define E1_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(E2)
-    #define E2_MICROSTEPS      128
-    #define E2_OVERCURRENT    2000
-    #define E2_STALLCURRENT   1500
-    #define E2_MAX_VOLTAGE     127
-    #define E2_CHAIN_POS        -1
-    #define E2_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(E3)
-    #define E3_MICROSTEPS      128
-    #define E3_OVERCURRENT    2000
-    #define E3_STALLCURRENT   1500
-    #define E3_MAX_VOLTAGE     127
-    #define E3_CHAIN_POS        -1
-    #define E3_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(E4)
-    #define E4_MICROSTEPS      128
-    #define E4_OVERCURRENT    2000
-    #define E4_STALLCURRENT   1500
-    #define E4_MAX_VOLTAGE     127
-    #define E4_CHAIN_POS        -1
-    #define E4_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(E5)
-    #define E5_MICROSTEPS      128
-    #define E5_OVERCURRENT    2000
-    #define E5_STALLCURRENT   1500
-    #define E5_MAX_VOLTAGE     127
-    #define E5_CHAIN_POS        -1
-    #define E5_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(E6)
-    #define E6_MICROSTEPS      128
-    #define E6_OVERCURRENT    2000
-    #define E6_STALLCURRENT   1500
-    #define E6_MAX_VOLTAGE     127
-    #define E6_CHAIN_POS        -1
-    #define E6_SLEW_RATE         1
-  #endif
-
-  #if AXIS_IS_L64XX(E7)
-    #define E7_MICROSTEPS      128
-    #define E7_OVERCURRENT    2000
-    #define E7_STALLCURRENT   1500
-    #define E7_MAX_VOLTAGE     127
-    #define E7_CHAIN_POS        -1
-    #define E7_SLEW_RATE         1
-  #endif
-
-  /**
-   * Monitor L6470 drivers for error conditions like over temperature and over current.
-   * In the case of over temperature Marlin can decrease the drive until the error condition clears.
-   * Other detected conditions can be used to stop the current print.
-   * Relevant G-codes:
-   * M906 - I1/2/3/4/5  Set or get motor drive level using axis codes X, Y, Z, E. Report values if no axis codes given.
-   *         I not present or I0 or I1 - X, Y, Z or E0
-   *         I2 - X2, Y2, Z2 or E1
-   *         I3 - Z3 or E3
-   *         I4 - Z4 or E4
-   *         I5 - E5
-   * M916 - Increase drive level until get thermal warning
-   * M917 - Find minimum current thresholds
-   * M918 - Increase speed until max or error
-   * M122 S0/1 - Report driver parameters
-   */
-  //#define MONITOR_L6470_DRIVER_STATUS
-
-  #if ENABLED(MONITOR_L6470_DRIVER_STATUS)
-    #define KVAL_HOLD_STEP_DOWN     1
-    //#define L6470_STOP_ON_ERROR
-  #endif
-
-#endif // HAS_L64XX
+#endif // HAS_TRINAMIC_CONFIG || HAS_TMC26X
 
 // @section i2cbus
 
@@ -2836,7 +3271,7 @@
   #define I2C_SLAVE_ADDRESS  0  // Set a value from 8 to 127 to act as a slave
 #endif
 
-// @section extras
+// @section photo
 
 /**
  * Photo G-code
@@ -2879,6 +3314,8 @@
   #endif
 #endif
 
+// @section cnc
+
 /**
  * Spindle & Laser control
  *
@@ -2892,22 +3329,46 @@
  * You'll need to select a pin for the ON/OFF function and optionally choose a 0-5V
  * hardware PWM pin for the speed control and a pin for the rotation direction.
  *
- * See https://marlinfw.org/docs/configuration/laser_spindle.html for more config details.
+ * See https://marlinfw.org/docs/configuration/2.0.9/laser_spindle.html for more config details.
  */
 //#define SPINDLE_FEATURE
 //#define LASER_FEATURE
 #if EITHER(SPINDLE_FEATURE, LASER_FEATURE)
-  #define SPINDLE_LASER_ACTIVE_STATE    LOW    // Set to "HIGH" if the on/off function is active HIGH
-  #define SPINDLE_LASER_PWM             true   // Set to "true" if your controller supports setting the speed/power
-  #define SPINDLE_LASER_PWM_INVERT      false  // Set to "true" if the speed/power goes up when you want it to go slower
+  #define SPINDLE_LASER_ACTIVE_STATE    LOW    // Set to "HIGH" if SPINDLE_LASER_ENA_PIN is active HIGH
 
-  #define SPINDLE_LASER_FREQUENCY       2500   // (Hz) Spindle/laser frequency (only on supported HALs: AVR and LPC)
+  #define SPINDLE_LASER_USE_PWM                // Enable if your controller supports setting the speed/power
+  #if ENABLED(SPINDLE_LASER_USE_PWM)
+    #define SPINDLE_LASER_PWM_INVERT    false  // Set to "true" if the speed/power goes up when you want it to go slower
+    #define SPINDLE_LASER_FREQUENCY     2500   // (Hz) Spindle/laser frequency (only on supported HALs: AVR, ESP32, and LPC)
+                                               // ESP32: If SPINDLE_LASER_PWM_PIN is onboard then <=78125Hz. For I2S expander
+                                               //  the frequency determines the PWM resolution. 2500Hz = 0-100, 977Hz = 0-255, ...
+                                               //  (250000 / SPINDLE_LASER_FREQUENCY) = max value.
+  #endif
+
+  //#define AIR_EVACUATION                     // Cutter Vacuum / Laser Blower motor control with G-codes M10-M11
+  #if ENABLED(AIR_EVACUATION)
+    #define AIR_EVACUATION_ACTIVE       LOW    // Set to "HIGH" if the on/off function is active HIGH
+    //#define AIR_EVACUATION_PIN        42     // Override the default Cutter Vacuum or Laser Blower pin
+  #endif
+
+  //#define AIR_ASSIST                         // Air Assist control with G-codes M8-M9
+  #if ENABLED(AIR_ASSIST)
+    #define AIR_ASSIST_ACTIVE           LOW    // Active state on air assist pin
+    //#define AIR_ASSIST_PIN            44     // Override the default Air Assist pin
+  #endif
+
+  //#define SPINDLE_SERVO                      // A servo converting an angle to spindle power
+  #ifdef SPINDLE_SERVO
+    #define SPINDLE_SERVO_NR   0               // Index of servo used for spindle control
+    #define SPINDLE_SERVO_MIN 10               // Minimum angle for servo spindle
+  #endif
 
   /**
    * Speed / Power can be set ('M3 S') and displayed in terms of:
    *  - PWM255  (S0 - S255)
    *  - PERCENT (S0 - S100)
    *  - RPM     (S0 - S50000)  Best for use with a spindle
+   *  - SERVO   (S0 - S180)
    */
   #define CUTTER_POWER_UNIT PWM255
 
@@ -2938,94 +3399,110 @@
      * Speed/Power = (PWMDC / 255 * 100 - SPEED_POWER_INTERCEPT) / SPEED_POWER_SLOPE
      * PWMDC = (spdpwr - SPEED_POWER_MIN) / (SPEED_POWER_MAX - SPEED_POWER_MIN) / SPEED_POWER_SLOPE
      */
-    #define SPEED_POWER_INTERCEPT         0    // (%) 0-100 i.e., Minimum power percentage
-    #define SPEED_POWER_MIN            5000    // (RPM)
-    #define SPEED_POWER_MAX           30000    // (RPM) SuperPID router controller 0 - 30,000 RPM
-    #define SPEED_POWER_STARTUP       25000    // (RPM) M3/M4 speed/power default (with no arguments)
+    #if ENABLED(SPINDLE_LASER_USE_PWM)
+      #define SPEED_POWER_INTERCEPT       0    // (%) 0-100 i.e., Minimum power percentage
+      #define SPEED_POWER_MIN          5000    // (RPM)
+      #define SPEED_POWER_MAX         30000    // (RPM) SuperPID router controller 0 - 30,000 RPM
+      #define SPEED_POWER_STARTUP     25000    // (RPM) M3/M4 speed/power default (with no arguments)
+    #endif
 
   #else
 
-    #define SPEED_POWER_INTERCEPT         0    // (%) 0-100 i.e., Minimum power percentage
-    #define SPEED_POWER_MIN               0    // (%) 0-100
-    #define SPEED_POWER_MAX             100    // (%) 0-100
-    #define SPEED_POWER_STARTUP          80    // (%) M3/M4 speed/power default (with no arguments)
+    #if ENABLED(SPINDLE_LASER_USE_PWM)
+      #define SPEED_POWER_INTERCEPT       0    // (%) 0-100 i.e., Minimum power percentage
+      #define SPEED_POWER_MIN             0    // (%) 0-100
+      #define SPEED_POWER_MAX           100    // (%) 0-100
+      #define SPEED_POWER_STARTUP        80    // (%) M3/M4 speed/power default (with no arguments)
+    #endif
+
+    // Define the minimum and maximum test pulse time values for a laser test fire function
+    #define LASER_TEST_PULSE_MIN           1   // (ms) Used with Laser Control Menu
+    #define LASER_TEST_PULSE_MAX         999   // (ms) Caution: Menu may not show more than 3 characters
+
+    #define SPINDLE_LASER_POWERUP_DELAY   50   // (ms) Delay to allow the spindle/laser to come up to speed/power
+    #define SPINDLE_LASER_POWERDOWN_DELAY 50   // (ms) Delay to allow the spindle to stop
+
+   /**
+    * Laser Safety Timeout
+    *
+    * The laser should be turned off when there is no movement for a period of time.
+    * Consider material flammability, cut rate, and G-code order when setting this
+    * value. Too low and it could turn off during a very slow move; too high and
+    * the material could ignite.
+    */
+    #define LASER_SAFETY_TIMEOUT_MS     1000   // (ms)
 
     /**
-     * Enable inline laser power to be handled in the planner / stepper routines.
-     * Inline power is specified by the I (inline) flag in an M3 command (e.g., M3 S20 I)
-     * or by the 'S' parameter in G0/G1/G2/G3 moves (see LASER_MOVE_POWER).
+     * Any M3 or G1/2/3/5 command with the 'I' parameter enables continuous inline power mode.
      *
-     * This allows the laser to keep in perfect sync with the planner and removes
-     * the powerup/down delay since lasers require negligible time.
+     * e.g., 'M3 I' enables continuous inline power which is processed by the planner.
+     * Power is stored in move blocks and applied when blocks are processed by the Stepper ISR.
+     *
+     * 'M4 I' sets dynamic mode which uses the current feedrate to calculate a laser power OCR value.
+     *
+     * Any move in dynamic mode will use the current feedrate to calculate the laser power.
+     * Feed rates are set by the F parameter of a move command e.g. G1 X0 Y10 F6000
+     * Laser power would be calculated by bit shifting off 8 LSB's. In binary this is div 256.
+     * The calculation gives us ocr values from 0 to 255, values over F65535 will be set as 255 .
+     * More refined power control such as compesation for accell/decell will be addressed in future releases.
+     *
+     * M5 I clears inline mode and set power to 0, M5 sets the power output to 0 but leaves inline mode on.
      */
-    #define LASER_POWER_INLINE
 
-    #if ENABLED(LASER_POWER_INLINE)
-      /**
-       * Scale the laser's power in proportion to the movement rate.
-       *
-       * - Sets the entry power proportional to the entry speed over the nominal speed.
-       * - Ramps the power up every N steps to approximate the speed trapezoid.
-       * - Due to the limited power resolution this is only approximate.
-       */
-      #define LASER_POWER_INLINE_TRAPEZOID
+    /**
+     * Enable M3 commands for laser mode inline power planner syncing.
+     * This feature enables any M3 S-value to be injected into the block buffers while in
+     * CUTTER_MODE_CONTINUOUS. The option allows M3 laser power to be commited without waiting
+     * for a planner syncronization
+     */
+    //#define LASER_POWER_SYNC
 
-      /**
-       * Continuously calculate the current power (nominal_power * current_rate / nominal_rate).
-       * Required for accurate power with non-trapezoidal acceleration (e.g., S_CURVE_ACCELERATION).
-       * This is a costly calculation so this option is discouraged on 8-bit AVR boards.
-       *
-       * LASER_POWER_INLINE_TRAPEZOID_CONT_PER defines how many step cycles there are between power updates. If your
-       * board isn't able to generate steps fast enough (and you are using LASER_POWER_INLINE_TRAPEZOID_CONT), increase this.
-       * Note that when this is zero it means it occurs every cycle; 1 means a delay wait one cycle then run, etc.
-       */
-      //#define LASER_POWER_INLINE_TRAPEZOID_CONT
-
-      /**
-       * Stepper iterations between power updates. Increase this value if the board
-       * can't keep up with the processing demands of LASER_POWER_INLINE_TRAPEZOID_CONT.
-       * Disable (or set to 0) to recalculate power on every stepper iteration.
-       */
-      //#define LASER_POWER_INLINE_TRAPEZOID_CONT_PER 10
-
-      /**
-       * Include laser power in G0/G1/G2/G3/G5 commands with the 'S' parameter
-       */
-      //#define LASER_MOVE_POWER
-
-      #if ENABLED(LASER_MOVE_POWER)
-        // Turn off the laser on G0 moves with no power parameter.
-        // If a power parameter is provided, use that instead.
-        //#define LASER_MOVE_G0_OFF
-
-        // Turn off the laser on G28 homing.
-        //#define LASER_MOVE_G28_OFF
-      #endif
-
-      /**
-       * Inline flag inverted
-       *
-       * WARNING: M5 will NOT turn off the laser unless another move
-       *          is done (so G-code files must end with 'M5 I').
-       */
-      //#define LASER_POWER_INLINE_INVERT
-
-      /**
-       * Continuously apply inline power. ('M3 S3' == 'G1 S3' == 'M3 S3 I')
-       *
-       * The laser might do some weird things, so only enable this
-       * feature if you understand the implications.
-       */
-      //#define LASER_POWER_INLINE_CONTINUOUS
-
-    #else
-
-      #define SPINDLE_LASER_POWERUP_DELAY     50 // (ms) Delay to allow the spindle/laser to come up to speed/power
-      #define SPINDLE_LASER_POWERDOWN_DELAY   50 // (ms) Delay to allow the spindle to stop
+    /**
+     * Scale the laser's power in proportion to the movement rate.
+     *
+     * - Sets the entry power proportional to the entry speed over the nominal speed.
+     * - Ramps the power up every N steps to approximate the speed trapezoid.
+     * - Due to the limited power resolution this is only approximate.
+     */
+    //#define LASER_POWER_TRAP
 
+    //
+    // Laser I2C Ammeter (High precision INA226 low/high side module)
+    //
+    //#define I2C_AMMETER
+    #if ENABLED(I2C_AMMETER)
+      #define I2C_AMMETER_IMAX            0.1    // (Amps) Calibration value for the expected current range
+      #define I2C_AMMETER_SHUNT_RESISTOR  0.1    // (Ohms) Calibration shunt resistor value
     #endif
+
+    //
+    // Laser Coolant Flow Meter
+    //
+    //#define LASER_COOLANT_FLOW_METER
+    #if ENABLED(LASER_COOLANT_FLOW_METER)
+      #define FLOWMETER_PIN         20  // Requires an external interrupt-enabled pin (e.g., RAMPS 2,3,18,19,20,21)
+      #define FLOWMETER_PPL       5880  // (pulses/liter) Flow meter pulses-per-liter on the input pin
+      #define FLOWMETER_INTERVAL  1000  // (ms) Flow rate calculation interval in milliseconds
+      #define FLOWMETER_SAFETY          // Prevent running the laser without the minimum flow rate set below
+      #if ENABLED(FLOWMETER_SAFETY)
+        #define FLOWMETER_MIN_LITERS_PER_MINUTE 1.5 // (liters/min) Minimum flow required when enabled
+      #endif
+    #endif
+
   #endif
-#endif
+#endif // SPINDLE_FEATURE || LASER_FEATURE
+
+/**
+ * Synchronous Laser Control with M106/M107
+ *
+ * Marlin normally applies M106/M107 fan speeds at a time "soon after" processing
+ * a planner block. This is too inaccurate for a PWM/TTL laser attached to the fan
+ * header (as with some add-on laser kits). Enable this option to set fan/laser
+ * speeds with much more exact timing for improved print fidelity.
+ *
+ * NOTE: This option sacrifices some cooling fan speed options.
+ */
+//#define LASER_SYNCHRONOUS_M106_M107
 
 /**
  * Coolant Control
@@ -3042,6 +3519,8 @@
   #define COOLANT_FLOOD_INVERT false  // Set "true" if the on/off function is reversed
 #endif
 
+// @section filament width
+
 /**
  * Filament Width Sensor
  *
@@ -3075,6 +3554,8 @@
   //#define FILAMENT_LCD_DISPLAY
 #endif
 
+// @section power
+
 /**
  * Power Monitor
  * Monitor voltage (V) and/or current (A), and -when possible- power (W)
@@ -3086,13 +3567,31 @@
  */
 //#define POWER_MONITOR_CURRENT   // Monitor the system current
 //#define POWER_MONITOR_VOLTAGE   // Monitor the system voltage
-#if EITHER(POWER_MONITOR_CURRENT, POWER_MONITOR_VOLTAGE)
-  #define POWER_MONITOR_VOLTS_PER_AMP   0.05000   // Input voltage to the MCU analog pin per amp  - DO NOT apply more than ADC_VREF!
-  #define POWER_MONITOR_CURRENT_OFFSET -1         // Offset value for current sensors with linear function output
-  #define POWER_MONITOR_VOLTS_PER_VOLT  0.11786   // Input voltage to the MCU analog pin per volt - DO NOT apply more than ADC_VREF!
+
+#if ENABLED(POWER_MONITOR_CURRENT)
+  #define POWER_MONITOR_VOLTS_PER_AMP    0.05000  // Input voltage to the MCU analog pin per amp  - DO NOT apply more than ADC_VREF!
+  #define POWER_MONITOR_CURRENT_OFFSET   0        // Offset (in amps) applied to the calculated current
   #define POWER_MONITOR_FIXED_VOLTAGE   13.6      // Voltage for a current sensor with no voltage sensor (for power display)
 #endif
 
+#if ENABLED(POWER_MONITOR_VOLTAGE)
+  #define POWER_MONITOR_VOLTS_PER_VOLT  0.077933  // Input voltage to the MCU analog pin per volt - DO NOT apply more than ADC_VREF!
+  #define POWER_MONITOR_VOLTAGE_OFFSET  0         // Offset (in volts) applied to the calculated voltage
+#endif
+
+// @section safety
+
+/**
+ * Stepper Driver Anti-SNAFU Protection
+ *
+ * If the SAFE_POWER_PIN is defined for your board, Marlin will check
+ * that stepper drivers are properly plugged in before applying power.
+ * Disable protection if your stepper drivers don't support the feature.
+ */
+//#define DISABLE_DRIVER_SAFE_POWER_PROTECT
+
+// @section cnc
+
 /**
  * CNC Coordinate Systems
  *
@@ -3101,10 +3600,26 @@
  */
 //#define CNC_COORDINATE_SYSTEMS
 
+// @section reporting
+
+/**
+ * Auto-report fan speed with M123 S
+ * Requires fans with tachometer pins
+ */
+//#define AUTO_REPORT_FANS
+
 /**
  * Auto-report temperatures with M155 S
  */
 #define AUTO_REPORT_TEMPERATURES
+#if ENABLED(AUTO_REPORT_TEMPERATURES) && TEMP_SENSOR_REDUNDANT
+  //#define AUTO_REPORT_REDUNDANT // Include the "R" sensor in the auto-report
+#endif
+
+/**
+ * Auto-report position with M154 S
+ */
+//#define AUTO_REPORT_POSITION
 
 /**
  * Include capabilities in M115 output
@@ -3114,6 +3629,8 @@
   //#define M115_GEOMETRY_REPORT
 #endif
 
+// @section security
+
 /**
  * Expected Printer Check
  * Add the M16 G-code to compare a string to the MACHINE_NAME.
@@ -3121,6 +3638,8 @@
  */
 //#define EXPECTED_PRINTER_CHECK
 
+// @section volumetrics
+
 /**
  * Disable all Volumetric extrusion options
  */
@@ -3149,14 +3668,7 @@
   #endif
 #endif
 
-/**
- * Enable this option for a leaner build of Marlin that removes all
- * workspace offsets, simplifying coordinate transformations, leveling, etc.
- *
- *  - M206 and M428 are disabled.
- *  - G92 will revert to its behavior from Marlin 1.0.
- */
-//#define NO_WORKSPACE_OFFSETS
+// @section reporting
 
 // Extra options for the M114 "Current Position" report
 //#define M114_DETAIL         // Use 'M114` for details to check planner calculations
@@ -3165,17 +3677,10 @@
 
 //#define REPORT_FAN_CHANGE   // Report the new fan speed when changed by M106 (and others)
 
-/**
- * Set the number of proportional font spaces required to fill up a typical character space.
- * This can help to better align the output of commands like `G29 O` Mesh Output.
- *
- * For clients that use a fixed-width font (like OctoPrint), leave this set to 1.0.
- * Otherwise, adjust according to your client and font.
- */
-#define PROPORTIONAL_FONT_RATIO 1.0
+// @section gcode
 
 /**
- * Spend 28 bytes of SRAM to optimize the GCode parser
+ * Spend 28 bytes of SRAM to optimize the G-code parser
  */
 #define FASTER_GCODE_PARSER
 
@@ -3183,10 +3688,23 @@
   //#define GCODE_QUOTED_STRINGS  // Support for quoted string parameters
 #endif
 
+// Support for MeatPack G-code compression (https://github.com/scottmudge/OctoPrint-MeatPack)
+//#define MEATPACK_ON_SERIAL_PORT_1
+//#define MEATPACK_ON_SERIAL_PORT_2
+
 //#define GCODE_CASE_INSENSITIVE  // Accept G-code sent to the firmware in lowercase
 
 //#define REPETIER_GCODE_M360     // Add commands originally from Repetier FW
 
+/**
+ * Enable this option for a leaner build of Marlin that removes all
+ * workspace offsets, simplifying coordinate transformations, leveling, etc.
+ *
+ *  - M206 and M428 are disabled.
+ *  - G92 will revert to its behavior from Marlin 1.0.
+ */
+//#define NO_WORKSPACE_OFFSETS
+
 /**
  * CNC G-code options
  * Support CNC-style G-code dialects used by laser cutters, drawing machine cams, etc.
@@ -3202,6 +3720,8 @@
   //#define VARIABLE_G0_FEEDRATE // The G0 feedrate is set by F in G0 motion mode
 #endif
 
+// @section gcode
+
 /**
  * Startup commands
  *
@@ -3222,31 +3742,109 @@
 #endif
 
 /**
- * User-defined menu items that execute custom GCode
+ * User-defined menu items to run custom G-code.
+ * Up to 25 may be defined, but the actual number is LCD-dependent.
  */
-//#define CUSTOM_USER_MENUS
-#if ENABLED(CUSTOM_USER_MENUS)
-  //#define CUSTOM_USER_MENU_TITLE "Custom Commands"
-  #define USER_SCRIPT_DONE "M117 User Script Done"
-  #define USER_SCRIPT_AUDIBLE_FEEDBACK
-  //#define USER_SCRIPT_RETURN  // Return to status screen after a script
 
-  #define USER_DESC_1 "Home & UBL Info"
-  #define USER_GCODE_1 "G28\nG29 W"
+// @section custom main menu
 
-  #define USER_DESC_2 "Preheat for " PREHEAT_1_LABEL
-  #define USER_GCODE_2 "M140 S" STRINGIFY(PREHEAT_1_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_1_TEMP_HOTEND)
+// Custom Menu: Main Menu
+//#define CUSTOM_MENU_MAIN
+#if ENABLED(CUSTOM_MENU_MAIN)
+  //#define CUSTOM_MENU_MAIN_TITLE "Custom Commands"
+  #define CUSTOM_MENU_MAIN_SCRIPT_DONE "M117 User Script Done"
+  #define CUSTOM_MENU_MAIN_SCRIPT_AUDIBLE_FEEDBACK
+  //#define CUSTOM_MENU_MAIN_SCRIPT_RETURN   // Return to status screen after a script
+  #define CUSTOM_MENU_MAIN_ONLY_IDLE         // Only show custom menu when the machine is idle
 
-  #define USER_DESC_3 "Preheat for " PREHEAT_2_LABEL
-  #define USER_GCODE_3 "M140 S" STRINGIFY(PREHEAT_2_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_2_TEMP_HOTEND)
+  #define MAIN_MENU_ITEM_1_DESC "Home & UBL Info"
+  #define MAIN_MENU_ITEM_1_GCODE "G28\nG29 W"
+  //#define MAIN_MENU_ITEM_1_CONFIRM          // Show a confirmation dialog before this action
 
-  #define USER_DESC_4 "Heat Bed/Home/Level"
-  #define USER_GCODE_4 "M140 S" STRINGIFY(PREHEAT_2_TEMP_BED) "\nG28\nG29"
+  #define MAIN_MENU_ITEM_2_DESC "Preheat for " PREHEAT_1_LABEL
+  #define MAIN_MENU_ITEM_2_GCODE "M140 S" STRINGIFY(PREHEAT_1_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_1_TEMP_HOTEND)
+  //#define MAIN_MENU_ITEM_2_CONFIRM
 
-  #define USER_DESC_5 "Home & Info"
-  #define USER_GCODE_5 "G28\nM503"
+  //#define MAIN_MENU_ITEM_3_DESC "Preheat for " PREHEAT_2_LABEL
+  //#define MAIN_MENU_ITEM_3_GCODE "M140 S" STRINGIFY(PREHEAT_2_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_2_TEMP_HOTEND)
+  //#define MAIN_MENU_ITEM_3_CONFIRM
+
+  //#define MAIN_MENU_ITEM_4_DESC "Heat Bed/Home/Level"
+  //#define MAIN_MENU_ITEM_4_GCODE "M140 S" STRINGIFY(PREHEAT_2_TEMP_BED) "\nG28\nG29"
+  //#define MAIN_MENU_ITEM_4_CONFIRM
+
+  //#define MAIN_MENU_ITEM_5_DESC "Home & Info"
+  //#define MAIN_MENU_ITEM_5_GCODE "G28\nM503"
+  //#define MAIN_MENU_ITEM_5_CONFIRM
 #endif
 
+// @section custom config menu
+
+// Custom Menu: Configuration Menu
+//#define CUSTOM_MENU_CONFIG
+#if ENABLED(CUSTOM_MENU_CONFIG)
+  //#define CUSTOM_MENU_CONFIG_TITLE "Custom Commands"
+  #define CUSTOM_MENU_CONFIG_SCRIPT_DONE "M117 Wireless Script Done"
+  #define CUSTOM_MENU_CONFIG_SCRIPT_AUDIBLE_FEEDBACK
+  //#define CUSTOM_MENU_CONFIG_SCRIPT_RETURN  // Return to status screen after a script
+  #define CUSTOM_MENU_CONFIG_ONLY_IDLE        // Only show custom menu when the machine is idle
+
+  #define CONFIG_MENU_ITEM_1_DESC "Wifi ON"
+  #define CONFIG_MENU_ITEM_1_GCODE "M118 [ESP110] WIFI-STA pwd=12345678"
+  //#define CONFIG_MENU_ITEM_1_CONFIRM        // Show a confirmation dialog before this action
+
+  #define CONFIG_MENU_ITEM_2_DESC "Bluetooth ON"
+  #define CONFIG_MENU_ITEM_2_GCODE "M118 [ESP110] BT pwd=12345678"
+  //#define CONFIG_MENU_ITEM_2_CONFIRM
+
+  //#define CONFIG_MENU_ITEM_3_DESC "Radio OFF"
+  //#define CONFIG_MENU_ITEM_3_GCODE "M118 [ESP110] OFF pwd=12345678"
+  //#define CONFIG_MENU_ITEM_3_CONFIRM
+
+  //#define CONFIG_MENU_ITEM_4_DESC "Wifi ????"
+  //#define CONFIG_MENU_ITEM_4_GCODE "M118 ????"
+  //#define CONFIG_MENU_ITEM_4_CONFIRM
+
+  //#define CONFIG_MENU_ITEM_5_DESC "Wifi ????"
+  //#define CONFIG_MENU_ITEM_5_GCODE "M118 ????"
+  //#define CONFIG_MENU_ITEM_5_CONFIRM
+#endif
+
+// @section custom buttons
+
+/**
+ * User-defined buttons to run custom G-code.
+ * Up to 25 may be defined.
+ */
+//#define CUSTOM_USER_BUTTONS
+#if ENABLED(CUSTOM_USER_BUTTONS)
+  //#define BUTTON1_PIN -1
+  #if PIN_EXISTS(BUTTON1)
+    #define BUTTON1_HIT_STATE     LOW       // State of the triggered button. NC=LOW. NO=HIGH.
+    #define BUTTON1_WHEN_PRINTING false     // Button allowed to trigger during printing?
+    #define BUTTON1_GCODE         "G28"
+    #define BUTTON1_DESC          "Homing"  // Optional string to set the LCD status
+  #endif
+
+  //#define BUTTON2_PIN -1
+  #if PIN_EXISTS(BUTTON2)
+    #define BUTTON2_HIT_STATE     LOW
+    #define BUTTON2_WHEN_PRINTING false
+    #define BUTTON2_GCODE         "M140 S" STRINGIFY(PREHEAT_1_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_1_TEMP_HOTEND)
+    #define BUTTON2_DESC          "Preheat for " PREHEAT_1_LABEL
+  #endif
+
+  //#define BUTTON3_PIN -1
+  #if PIN_EXISTS(BUTTON3)
+    #define BUTTON3_HIT_STATE     LOW
+    #define BUTTON3_WHEN_PRINTING false
+    #define BUTTON3_GCODE         "M140 S" STRINGIFY(PREHEAT_2_TEMP_BED) "\nM104 S" STRINGIFY(PREHEAT_2_TEMP_HOTEND)
+    #define BUTTON3_DESC          "Preheat for " PREHEAT_2_LABEL
+  #endif
+#endif
+
+// @section host
+
 /**
  * Host Action Commands
  *
@@ -3263,16 +3861,26 @@
  */
 //#define HOST_ACTION_COMMANDS
 #if ENABLED(HOST_ACTION_COMMANDS)
-  //#define HOST_PROMPT_SUPPORT
-  //#define HOST_START_MENU_ITEM  // Add a menu item that tells the host to start
+  //#define HOST_PAUSE_M76                // Tell the host to pause in response to M76
+  //#define HOST_PROMPT_SUPPORT           // Initiate host prompts to get user feedback
+  #if ENABLED(HOST_PROMPT_SUPPORT)
+    //#define HOST_STATUS_NOTIFICATIONS   // Send some status messages to the host as notifications
+  #endif
+  //#define HOST_START_MENU_ITEM          // Add a menu item that tells the host to start
+  //#define HOST_SHUTDOWN_MENU_ITEM       // Add a menu item that tells the host to shut down
 #endif
 
+// @section extras
+
 /**
  * Cancel Objects
  *
  * Implement M486 to allow Marlin to skip objects
  */
 //#define CANCEL_OBJECTS
+#if ENABLED(CANCEL_OBJECTS)
+  #define CANCEL_OBJECTS_REPORTING // Emit the current object as a status message
+#endif
 
 /**
  * I2C position encoders for closed loop control.
@@ -3285,6 +3893,7 @@
  * Alternative Supplier: https://reliabuild3d.com/
  *
  * Reliabuild encoders have been modified to improve reliability.
+ * @section i2c encoders
  */
 
 //#define I2C_POSITION_ENCODERS
@@ -3349,13 +3958,14 @@
    */
   #define I2CPE_MIN_UPD_TIME_MS     4                       // (ms) Minimum time between encoder checks.
 
-  // Use a rolling average to identify persistant errors that indicate skips, as opposed to vibration and noise.
+  // Use a rolling average to identify persistent errors that indicate skips, as opposed to vibration and noise.
   #define I2CPE_ERR_ROLLING_AVERAGE
 
 #endif // I2C_POSITION_ENCODERS
 
 /**
  * Analog Joystick(s)
+ * @section joystick
  */
 //#define JOYSTICK
 #if ENABLED(JOYSTICK)
@@ -3377,9 +3987,10 @@
 
 /**
  * Mechanical Gantry Calibration
- * Modern replacement for the Prusa TMC_Z_CALIBRATION.
+ * Modern replacement for the Průša TMC_Z_CALIBRATION.
  * Adds capability to work with any adjustable current drivers.
  * Implemented as G34 because M915 is deprecated.
+ * @section calibrate
  */
 //#define MECHANICAL_GANTRY_CALIBRATION
 #if ENABLED(MECHANICAL_GANTRY_CALIBRATION)
@@ -3388,17 +3999,29 @@
   #define GANTRY_CALIBRATION_FEEDRATE         500     // Feedrate for correction move
   //#define GANTRY_CALIBRATION_TO_MIN                 // Enable to calibrate Z in the MIN direction
 
-  //#define GANTRY_CALIBRATION_SAFE_POSITION  { X_CENTER, Y_CENTER } // Safe position for nozzle
+  //#define GANTRY_CALIBRATION_SAFE_POSITION XY_CENTER // Safe position for nozzle
   //#define GANTRY_CALIBRATION_XY_PARK_FEEDRATE 3000  // XY Park Feedrate - MMM
   //#define GANTRY_CALIBRATION_COMMANDS_PRE   ""
   #define GANTRY_CALIBRATION_COMMANDS_POST  "G28"     // G28 highly recommended to ensure an accurate position
 #endif
 
+/**
+ * Instant freeze / unfreeze functionality
+ * Potentially useful for emergency stop that allows being resumed.
+ * @section interface
+ */
+//#define FREEZE_FEATURE
+#if ENABLED(FREEZE_FEATURE)
+  //#define FREEZE_PIN 41   // Override the default (KILL) pin here
+  #define FREEZE_STATE LOW  // State of pin indicating freeze
+#endif
+
 /**
  * MAX7219 Debug Matrix
  *
  * Add support for a low-cost 8x8 LED Matrix based on the Max7219 chip as a realtime status display.
  * Requires 3 signal wires. Some useful debug options are included to demonstrate its usage.
+ * @section debug matrix
  */
 //#define MAX7219_DEBUG
 #if ENABLED(MAX7219_DEBUG)
@@ -3411,7 +4034,8 @@
   #define MAX7219_NUMBER_UNITS 1   // Number of Max7219 units in chain.
   #define MAX7219_ROTATE       0   // Rotate the display clockwise (in multiples of +/- 90°)
                                    // connector at:  right=0   bottom=-90  top=90  left=180
-  //#define MAX7219_REVERSE_ORDER  // The individual LED matrix units may be in reversed order
+  //#define MAX7219_REVERSE_ORDER  // The order of the LED matrix units may be reversed
+  //#define MAX7219_REVERSE_EACH   // The LEDs in each matrix unit row may be reversed
   //#define MAX7219_SIDE_BY_SIDE   // Big chip+matrix boards can be chained side-by-side
 
   /**
@@ -3419,31 +4043,42 @@
    * If you add more debug displays, be careful to avoid conflicts!
    */
   #define MAX7219_DEBUG_PRINTER_ALIVE    // Blink corner LED of 8x8 matrix to show that the firmware is functioning
-  #define MAX7219_DEBUG_PLANNER_HEAD  3  // Show the planner queue head position on this and the next LED matrix row
-  #define MAX7219_DEBUG_PLANNER_TAIL  5  // Show the planner queue tail position on this and the next LED matrix row
+  #define MAX7219_DEBUG_PLANNER_HEAD  2  // Show the planner queue head position on this and the next LED matrix row
+  #define MAX7219_DEBUG_PLANNER_TAIL  4  // Show the planner queue tail position on this and the next LED matrix row
 
   #define MAX7219_DEBUG_PLANNER_QUEUE 0  // Show the current planner queue depth on this and the next LED matrix row
                                          // If you experience stuttering, reboots, etc. this option can reveal how
                                          // tweaks made to the configuration are affecting the printer in real-time.
+  #define MAX7219_DEBUG_PROFILE       6  // Display the fraction of CPU time spent in profiled code on this LED matrix
+                                         // row. By default idle() is profiled so this shows how "idle" the processor is.
+                                         // See class CodeProfiler.
 #endif
 
 /**
  * NanoDLP Sync support
  *
- * Add support for Synchronized Z moves when using with NanoDLP. G0/G1 axis moves will output "Z_move_comp"
- * string to enable synchronization with DLP projector exposure. This change will allow to use
- * [[WaitForDoneMessage]] instead of populating your gcode with M400 commands
+ * Support for Synchronized Z moves when used with NanoDLP. G0/G1 axis moves will
+ * output a "Z_move_comp" string to enable synchronization with DLP projector exposure.
+ * This feature allows you to use [[WaitForDoneMessage]] instead of M400 commands.
+ * @section nanodlp
  */
 //#define NANODLP_Z_SYNC
 #if ENABLED(NANODLP_Z_SYNC)
-  //#define NANODLP_ALL_AXIS  // Enables "Z_move_comp" output on any axis move.
-                              // Default behavior is limited to Z axis only.
+  //#define NANODLP_ALL_AXIS  // Send a "Z_move_comp" report for any axis move (not just Z).
+#endif
+
+/**
+ * Ethernet. Use M552 to enable and set the IP address.
+ * @section network
+ */
+#if HAS_ETHERNET
+  #define MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xF0, 0x0D }  // A MAC address unique to your network
 #endif
 
 /**
  * WiFi Support (Espressif ESP32 WiFi)
  */
-//#define WIFISUPPORT         // Marlin embedded WiFi managenent
+//#define WIFISUPPORT         // Marlin embedded WiFi management
 //#define ESP3D_WIFISUPPORT   // ESP3D Library WiFi management (https://github.com/luc-github/ESP3DLib)
 
 #if EITHER(WIFISUPPORT, ESP3D_WIFISUPPORT)
@@ -3462,17 +4097,29 @@
   //#include "Configuration_Secure.h" // External file with WiFi SSID / Password
 #endif
 
-/**
- * Průša Multi-Material Unit v2
- * Enable in Configuration.h
- */
-#if ENABLED(PRUSA_MMU2)
+// @section multi-material
 
+/**
+ * Průša Multi-Material Unit (MMU)
+ * Enable in Configuration.h
+ *
+ * These devices allow a single stepper driver on the board to drive
+ * multi-material feeders with any number of stepper motors.
+ */
+#if HAS_PRUSA_MMU1
+  /**
+   * This option only allows the multiplexer to switch on tool-change.
+   * Additional options to configure custom E moves are pending.
+   *
+   * Override the default DIO selector pins here, if needed.
+   * Some pins files may provide defaults for these pins.
+   */
+  //#define E_MUX0_PIN 40  // Always Required
+  //#define E_MUX1_PIN 42  // Needed for 3 to 8 inputs
+  //#define E_MUX2_PIN 44  // Needed for 5 to 8 inputs
+#elif HAS_PRUSA_MMU2
   // Serial port used for communication with MMU2.
-  // For AVR enable the UART port used for the MMU. (e.g., mmuSerial)
-  // For 32-bit boards check your HAL for available serial ports. (e.g., Serial2)
   #define MMU2_SERIAL_PORT 2
-  #define MMU2_SERIAL mmuSerial
 
   // Use hardware reset for MMU if a pin is defined for it
   //#define MMU2_RST_PIN 23
@@ -3485,7 +4132,7 @@
 
   // Add an LCD menu for MMU2
   //#define MMU2_MENUS
-  #if ENABLED(MMU2_MENUS)
+  #if EITHER(MMU2_MENUS, HAS_PRUSA_MMU2S)
     // Settings for filament load / unload from the LCD menu.
     // This is for Průša MK3-style extruders. Customize for your hardware.
     #define MMU2_FILAMENTCHANGE_EJECT_FEED 80.0
@@ -3510,29 +4157,12 @@
       { -50.0, 2000 }
   #endif
 
-  /**
-   * MMU Extruder Sensor
-   *
-   * Support for a Průša (or other) IR Sensor to detect filament near the extruder
-   * and make loading more reliable. Suitable for an extruder equipped with a filament
-   * sensor less than 38mm from the gears.
-   *
-   * During loading the extruder will stop when the sensor is triggered, then do a last
-   * move up to the gears. If no filament is detected, the MMU2 can make some more attempts.
-   * If all attempts fail, a filament runout will be triggered.
-   */
-  //#define MMU_EXTRUDER_SENSOR
-  #if ENABLED(MMU_EXTRUDER_SENSOR)
-    #define MMU_LOADING_ATTEMPTS_NR 5 // max. number of attempts to load filament if first load fail
-  #endif
-
   /**
    * Using a sensor like the MMU2S
    * This mode requires a MK3S extruder with a sensor at the extruder idler, like the MMU2S.
    * See https://help.prusa3d.com/en/guide/3b-mk3s-mk2-5s-extruder-upgrade_41560, step 11
    */
-  //#define PRUSA_MMU2_S_MODE
-  #if ENABLED(PRUSA_MMU2_S_MODE)
+  #if HAS_PRUSA_MMU2S
     #define MMU2_C0_RETRY   5             // Number of retries (total time = timeout*retries)
 
     #define MMU2_CAN_LOAD_FEEDRATE 800    // (mm/min)
@@ -3548,14 +4178,33 @@
     #define MMU2_CAN_LOAD_INCREMENT_SEQUENCE \
       { -MMU2_CAN_LOAD_INCREMENT, MMU2_CAN_LOAD_FEEDRATE }
 
+  #else
+
+    /**
+     * MMU1 Extruder Sensor
+     *
+     * Support for a Průša (or other) IR Sensor to detect filament near the extruder
+     * and make loading more reliable. Suitable for an extruder equipped with a filament
+     * sensor less than 38mm from the gears.
+     *
+     * During loading the extruder will stop when the sensor is triggered, then do a last
+     * move up to the gears. If no filament is detected, the MMU2 can make some more attempts.
+     * If all attempts fail, a filament runout will be triggered.
+     */
+    //#define MMU_EXTRUDER_SENSOR
+    #if ENABLED(MMU_EXTRUDER_SENSOR)
+      #define MMU_LOADING_ATTEMPTS_NR 5 // max. number of attempts to load filament if first load fail
+    #endif
+
   #endif
 
   //#define MMU2_DEBUG  // Write debug info to serial output
 
-#endif // PRUSA_MMU2
+#endif // HAS_PRUSA_MMU2
 
 /**
  * Advanced Print Counter settings
+ * @section stats
  */
 #if ENABLED(PRINTCOUNTER)
   #define SERVICE_WARNING_BUZZES  3
@@ -3585,5 +4234,32 @@
 //
 //#define PINS_DEBUGGING
 
+// Enable Tests that will run at startup and produce a report
+//#define MARLIN_TEST_BUILD
+
 // Enable Marlin dev mode which adds some special commands
 //#define MARLIN_DEV_MODE
+
+#if ENABLED(MARLIN_DEV_MODE)
+  /**
+   * D576 - Buffer Monitoring
+   * To help diagnose print quality issues stemming from empty command buffers.
+   */
+  //#define BUFFER_MONITORING
+#endif
+
+/**
+ * Postmortem Debugging captures misbehavior and outputs the CPU status and backtrace to serial.
+ * When running in the debugger it will break for debugging. This is useful to help understand
+ * a crash from a remote location. Requires ~400 bytes of SRAM and 5Kb of flash.
+ */
+//#define POSTMORTEM_DEBUGGING
+
+/**
+ * Software Reset options
+ */
+//#define SOFT_RESET_VIA_SERIAL         // 'KILL' and '^X' commands will soft-reset the controller
+//#define SOFT_RESET_ON_KILL            // Use a digital button to soft-reset the controller after KILL
+
+// Report uncleaned reset reason from register r2 instead of MCUSR. Supported by Optiboot on AVR.
+//#define OPTIBOOT_RESET_REASON
diff --git a/Marlin/Makefile b/Marlin/Makefile
index 49cb960b92..ca7cacaa6a 100644
--- a/Marlin/Makefile
+++ b/Marlin/Makefile
@@ -109,8 +109,8 @@ LIQUID_TWI2        ?= 0
 # This defines if Wire is needed
 WIRE               ?= 0
 
-# This defines if Tone is needed (i.e SPEAKER is defined in Configuration.h)
-# Disabling this (and SPEAKER) saves approximatively 350 bytes of memory.
+# This defines if Tone is needed (i.e., SPEAKER is defined in Configuration.h)
+# Disabling this (and SPEAKER) saves approximately 350 bytes of memory.
 TONE               ?= 1
 
 # This defines if U8GLIB is needed (may require RELOC_WORKAROUND)
@@ -132,7 +132,7 @@ CC_MIN:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC_MINOR__ | cut -f3 -d\ )
 CC_PATCHLEVEL:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC_PATCHLEVEL__ | cut -f3 -d\ )
 CC_VER:=$(shell echo $$(( $(CC_MAJ) * 10000 + $(CC_MIN) * 100 + $(CC_PATCHLEVEL) )))
 ifeq ($(shell test $(CC_VER) -lt 40901 && echo 1),1)
-  @echo This version of GCC is likely broken. Enabling relocation workaround.
+  $(warning This GCC version $(CC_VER) is likely broken. Enabling relocation workaround.)
   RELOC_WORKAROUND = 1
 endif
 
@@ -207,11 +207,11 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1105)
 else ifeq ($(HARDWARE_MOTHERBOARD),1106)
 # MKS BASE v1.0
 else ifeq ($(HARDWARE_MOTHERBOARD),1107)
-# MKS v1.4 with A4982 stepper drivers
+# MKS BASE v1.4 with Allegro A4982 stepper drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1108)
-# MKS v1.5 with Allegro A4982 stepper drivers
+# MKS BASE v1.5 with Allegro A4982 stepper drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1109)
-# MKS v1.6 with Allegro A4982 stepper drivers
+# MKS BASE v1.6 with Allegro A4982 stepper drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1110)
 # MKS BASE 1.0 with Heroic HR4982 stepper drivers
 else ifeq ($(HARDWARE_MOTHERBOARD),1111)
@@ -219,93 +219,110 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1111)
 else ifeq ($(HARDWARE_MOTHERBOARD),1112)
 # MKS GEN L
 else ifeq ($(HARDWARE_MOTHERBOARD),1113)
-# zrib V2.0 control board (Chinese knock off RAMPS replica)
-else ifeq ($(HARDWARE_MOTHERBOARD),1114)
 # BigTreeTech or BIQU KFB2.0
+else ifeq ($(HARDWARE_MOTHERBOARD),1114)
+# zrib V2.0 (Chinese RAMPS replica)
 else ifeq ($(HARDWARE_MOTHERBOARD),1115)
-# Felix 2.0+ Electronics Board (RAMPS like)
+# zrib V5.2 (Chinese RAMPS replica)
 else ifeq ($(HARDWARE_MOTHERBOARD),1116)
-# Invent-A-Part RigidBoard
+# Felix 2.0+ Electronics Board (RAMPS like)
 else ifeq ($(HARDWARE_MOTHERBOARD),1117)
-# Invent-A-Part RigidBoard V2
+# Invent-A-Part RigidBoard
 else ifeq ($(HARDWARE_MOTHERBOARD),1118)
-# Sainsmart 2-in-1 board
+# Invent-A-Part RigidBoard V2
 else ifeq ($(HARDWARE_MOTHERBOARD),1119)
-# Ultimaker
+# Sainsmart 2-in-1 board
 else ifeq ($(HARDWARE_MOTHERBOARD),1120)
-# Ultimaker (Older electronics. Pre 1.5.4. This is rare)
+# Ultimaker
 else ifeq ($(HARDWARE_MOTHERBOARD),1121)
+# Ultimaker (Older electronics. Pre 1.5.4. This is rare)
+else ifeq ($(HARDWARE_MOTHERBOARD),1122)
   MCU              ?= atmega1280
   PROG_MCU         ?= m1280
-
 # Azteeg X3
-else ifeq ($(HARDWARE_MOTHERBOARD),1122)
-# Azteeg X3 Pro
 else ifeq ($(HARDWARE_MOTHERBOARD),1123)
-# Ultimainboard 2.x (Uses TEMP_SENSOR 20)
+# Azteeg X3 Pro
 else ifeq ($(HARDWARE_MOTHERBOARD),1124)
-# Rumba
+# Ultimainboard 2.x (Uses TEMP_SENSOR 20)
 else ifeq ($(HARDWARE_MOTHERBOARD),1125)
-# Raise3D Rumba
+# Rumba
 else ifeq ($(HARDWARE_MOTHERBOARD),1126)
-# Rapide Lite RL200 Rumba
+# Raise3D N series Rumba derivative
 else ifeq ($(HARDWARE_MOTHERBOARD),1127)
-# Formbot T-Rex 2 Plus
+# Rapide Lite 200 (v1, low-cost RUMBA clone with drv)
 else ifeq ($(HARDWARE_MOTHERBOARD),1128)
-# Formbot T-Rex 3
+# Formbot T-Rex 2 Plus
 else ifeq ($(HARDWARE_MOTHERBOARD),1129)
-# Formbot Raptor
+# Formbot T-Rex 3
 else ifeq ($(HARDWARE_MOTHERBOARD),1130)
-# Formbot Raptor 2
+# Formbot Raptor
 else ifeq ($(HARDWARE_MOTHERBOARD),1131)
-# bq ZUM Mega 3D
+# Formbot Raptor 2
 else ifeq ($(HARDWARE_MOTHERBOARD),1132)
-# MakeBoard Mini v2.1.2 is a control board sold by MicroMake
+# bq ZUM Mega 3D
 else ifeq ($(HARDWARE_MOTHERBOARD),1133)
-# TriGorilla Anycubic version 1.3 based on RAMPS EFB
+# MakeBoard Mini v2.1.2 by MicroMake
 else ifeq ($(HARDWARE_MOTHERBOARD),1134)
-# TriGorilla Anycubic version 1.4 based on RAMPS EFB
+# TriGorilla Anycubic version 1.3-based on RAMPS EFB
 else ifeq ($(HARDWARE_MOTHERBOARD),1135)
-# TriGorilla Anycubic version 1.4 Rev 1.1
+#   ... Ver 1.4
 else ifeq ($(HARDWARE_MOTHERBOARD),1136)
-# Creality: Ender-4, CR-8
+#   ... Rev 1.1 (new servo pin order)
 else ifeq ($(HARDWARE_MOTHERBOARD),1137)
-# Creality: CR10S, CR20, CR-X
+# Creality: Ender-4, CR-8
 else ifeq ($(HARDWARE_MOTHERBOARD),1138)
-# Dagoma F5
+# Creality: CR10S, CR20, CR-X
 else ifeq ($(HARDWARE_MOTHERBOARD),1139)
-# FYSETC F6 1.3
+# Dagoma F5
 else ifeq ($(HARDWARE_MOTHERBOARD),1140)
-# FYSETC F6 1.5
+# FYSETC F6 1.3
 else ifeq ($(HARDWARE_MOTHERBOARD),1141)
-# Duplicator i3 Plus
+# FYSETC F6 1.4
 else ifeq ($(HARDWARE_MOTHERBOARD),1142)
-# VORON
+# Wanhao Duplicator i3 Plus
 else ifeq ($(HARDWARE_MOTHERBOARD),1143)
-# TRONXY V3 1.0
+# VORON Design
 else ifeq ($(HARDWARE_MOTHERBOARD),1144)
-# Z-Bolt X Series
+# Tronxy TRONXY-V3-1.0
 else ifeq ($(HARDWARE_MOTHERBOARD),1145)
-# TT OSCAR
+# Z-Bolt X Series
 else ifeq ($(HARDWARE_MOTHERBOARD),1146)
-# Overlord/Overlord Pro
+# TT OSCAR
 else ifeq ($(HARDWARE_MOTHERBOARD),1147)
-# ADIMLab Gantry v1
+# Overlord/Overlord Pro
 else ifeq ($(HARDWARE_MOTHERBOARD),1148)
-# ADIMLab Gantry v2
+# ADIMLab Gantry v1
 else ifeq ($(HARDWARE_MOTHERBOARD),1149)
-# BIQU Tango V1
+# ADIMLab Gantry v2
 else ifeq ($(HARDWARE_MOTHERBOARD),1150)
-# MKS GEN L V2
+# BIQU Tango V1
 else ifeq ($(HARDWARE_MOTHERBOARD),1151)
-# MKS GEN L V2.1
+# MKS GEN L V2
 else ifeq ($(HARDWARE_MOTHERBOARD),1152)
-# Copymaster 3D
+# MKS GEN L V2.1
 else ifeq ($(HARDWARE_MOTHERBOARD),1153)
-# Ortur 4
+# Copymaster 3D
 else ifeq ($(HARDWARE_MOTHERBOARD),1154)
-# Tenlog D3 Hero
+# Ortur 4
 else ifeq ($(HARDWARE_MOTHERBOARD),1155)
+# Tenlog D3 Hero IDEX printer
+else ifeq ($(HARDWARE_MOTHERBOARD),1156)
+# Tenlog D3,5,6 Pro IDEX printers
+else ifeq ($(HARDWARE_MOTHERBOARD),1157)
+# Ramps S 1.2 by Sakul.cz (Power outputs: Hotend0, Hotend1, Fan, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1158)
+# Ramps S 1.2 by Sakul.cz (Power outputs: Hotend0, Hotend1, Hotend2, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1159)
+# Ramps S 1.2 by Sakul.cz (Power outputs: Hotend, Fan0, Fan1, Bed)
+else ifeq ($(HARDWARE_MOTHERBOARD),1160)
+# Longer LK1 PRO / Alfawise U20 Pro (PRO version)
+else ifeq ($(HARDWARE_MOTHERBOARD),1161)
+# Longer LKx PRO / Alfawise Uxx Pro (PRO version)
+else ifeq ($(HARDWARE_MOTHERBOARD),1162)
+# Zonestar zrib V5.3 (Chinese RAMPS replica)
+else ifeq ($(HARDWARE_MOTHERBOARD),1163)
+# Pxmalion Core I3
+else ifeq ($(HARDWARE_MOTHERBOARD),1164)
 
 #
 # RAMBo and derivatives
@@ -323,6 +340,8 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1203)
 else ifeq ($(HARDWARE_MOTHERBOARD),1204)
 # abee Scoovo X9H
 else ifeq ($(HARDWARE_MOTHERBOARD),1205)
+# Rambo ThinkerV2
+else ifeq ($(HARDWARE_MOTHERBOARD),1206)
 
 #
 # Other ATmega1280, ATmega2560
@@ -356,20 +375,38 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1311)
 else ifeq ($(HARDWARE_MOTHERBOARD),1312)
 # Mega controller
 else ifeq ($(HARDWARE_MOTHERBOARD),1313)
-# Geeetech GT2560 Rev B for Mecreator2
+# Geeetech GT2560 Rev A
 else ifeq ($(HARDWARE_MOTHERBOARD),1314)
-# Geeetech GT2560 Rev. A
+# Geeetech GT2560 Rev A+ (with auto level probe)
 else ifeq ($(HARDWARE_MOTHERBOARD),1315)
-# Geeetech GT2560 Rev. A+ (with auto level probe)
+# Geeetech GT2560 Rev B
 else ifeq ($(HARDWARE_MOTHERBOARD),1316)
-# Geeetech GT2560 Rev B for A10(M/D)
+# Geeetech GT2560 Rev B for A10(M/T/D)
 else ifeq ($(HARDWARE_MOTHERBOARD),1317)
-# Geeetech GT2560 Rev B for A20(M/D)
+# Geeetech GT2560 Rev B for A10(M/T/D)
 else ifeq ($(HARDWARE_MOTHERBOARD),1318)
-# Einstart retrofit
+# Geeetech GT2560 Rev B for Mecreator2
 else ifeq ($(HARDWARE_MOTHERBOARD),1319)
-# Wanhao 0ne+ i3 Mini
+# Geeetech GT2560 Rev B for A20(M/T/D)
 else ifeq ($(HARDWARE_MOTHERBOARD),1320)
+# Einstart retrofit
+else ifeq ($(HARDWARE_MOTHERBOARD),1321)
+# Wanhao 0ne+ i3 Mini
+else ifeq ($(HARDWARE_MOTHERBOARD),1322)
+# Leapfrog Xeed 2015
+else ifeq ($(HARDWARE_MOTHERBOARD),1323)
+# PICA Shield (original version)
+else ifeq ($(HARDWARE_MOTHERBOARD),1324)
+# PICA Shield (rev C or later)
+else ifeq ($(HARDWARE_MOTHERBOARD),1325)
+# Intamsys 4.0 (Funmat HT)
+else ifeq ($(HARDWARE_MOTHERBOARD),1326)
+# Malyan M180 Mainboard Version 2 (no display function, direct G-code only)
+else ifeq ($(HARDWARE_MOTHERBOARD),1327)
+# Geeetech GT2560 Rev B for A20(M/T/D)
+else ifeq ($(HARDWARE_MOTHERBOARD),1328)
+# Mega controller & Protoneer CNC Shield V3.00
+else ifeq ($(HARDWARE_MOTHERBOARD),1329)
 
 #
 # ATmega1281, ATmega2561
@@ -443,6 +480,11 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1510)
   HARDWARE_VARIANT ?= Sanguino
   MCU              ?= atmega1284p
   PROG_MCU         ?= m1284p
+# ZoneStar ZMIB V2
+else ifeq ($(HARDWARE_MOTHERBOARD),1511)
+  HARDWARE_VARIANT ?= Sanguino
+  MCU              ?= atmega1284p
+  PROG_MCU         ?= m1284p
 
 #
 # Other ATmega644P, ATmega644, ATmega1284P
@@ -991,5 +1033,5 @@ clean:
 
 .PHONY: all build elf hex eep lss sym program coff extcoff clean depend sizebefore sizeafter
 
-# Automaticaly include the dependency files created by gcc
+# Automatically include the dependency files created by gcc
 -include ${patsubst %.o, %.d, ${OBJ}}
diff --git a/Marlin/Version.h b/Marlin/Version.h
index 60e068ba35..3514add53b 100644
--- a/Marlin/Version.h
+++ b/Marlin/Version.h
@@ -28,7 +28,7 @@
 /**
  * Marlin release version identifier
  */
-//#define SHORT_BUILD_VERSION "2.0.7.2"
+//#define SHORT_BUILD_VERSION "2.1.2"
 
 /**
  * Verbose version identifier which should contain a reference to the location
@@ -41,7 +41,7 @@
  * here we define this default string as the date where the latest release
  * version was tagged.
  */
-//#define STRING_DISTRIBUTION_DATE "2020-07-09"
+//#define STRING_DISTRIBUTION_DATE "2022-12-17"
 
 /**
  * Defines a generic printer name to be output to the LCD after booting Marlin.
@@ -54,7 +54,7 @@
  * has a distinct Github fork— the Source Code URL should just be the main
  * Marlin repository.
  */
-//#define SOURCE_CODE_URL "https://github.com/MarlinFirmware/Marlin"
+//#define SOURCE_CODE_URL "github.com/MarlinFirmware/Marlin"
 
 /**
  * Default generic printer UUID.
@@ -65,7 +65,7 @@
  * The WEBSITE_URL is the location where users can get more information such as
  * documentation about a specific Marlin release.
  */
-//#define WEBSITE_URL "https://marlinfw.org"
+//#define WEBSITE_URL "marlinfw.org"
 
 /**
  * Set the vendor info the serial USB interface, if changable
diff --git a/Marlin/config.ini b/Marlin/config.ini
new file mode 100644
index 0000000000..0fb9fb0c93
--- /dev/null
+++ b/Marlin/config.ini
@@ -0,0 +1,211 @@
+#
+# Marlin Firmware
+# config.ini - Options to apply before the build
+#
+[config:base]
+ini_use_config                           = none
+
+# Load all config: sections in this file
+;ini_use_config                          = all
+# Load config file relative to Marlin/
+;ini_use_config                          = another.ini
+# Download configurations from GitHub
+;ini_use_config                          = example/Creality/Ender-5 Plus @ bugfix-2.1.x
+# Download configurations from your server
+;ini_use_config                          = https://me.myserver.com/path/to/configs
+# Evaluate config:base and do a config dump
+;ini_use_config                          = base
+;config_export                           = 2
+
+[config:minimal]
+motherboard                              = BOARD_RAMPS_14_EFB
+serial_port                              = 0
+baudrate                                 = 250000
+
+use_watchdog                             = on
+thermal_protection_hotends               = on
+thermal_protection_hysteresis            = 4
+thermal_protection_period                = 40
+
+bufsize                                  = 4
+block_buffer_size                        = 16
+max_cmd_size                             = 96
+
+extruders                                = 1
+temp_sensor_0                            = 1
+
+temp_hysteresis                          = 3
+heater_0_mintemp                         = 5
+heater_0_maxtemp                         = 275
+preheat_1_temp_hotend                    = 180
+
+bang_max                                 = 255
+pidtemp                                  = on
+pid_k1                                   = 0.95
+pid_max                                  = BANG_MAX
+pid_functional_range                     = 10
+
+default_kp                               = 22.20
+default_ki                               = 1.08
+default_kd                               = 114.00
+
+x_driver_type                            = A4988
+y_driver_type                            = A4988
+z_driver_type                            = A4988
+e0_driver_type                           = A4988
+
+x_bed_size                               = 200
+x_min_pos                                = 0
+x_max_pos                                = X_BED_SIZE
+
+y_bed_size                               = 200
+y_min_pos                                = 0
+y_max_pos                                = Y_BED_SIZE
+
+z_min_pos                                = 0
+z_max_pos                                = 200
+
+x_home_dir                               = -1
+y_home_dir                               = -1
+z_home_dir                               = -1
+
+use_xmin_plug                            = on
+use_ymin_plug                            = on
+use_zmin_plug                            = on
+
+x_min_endstop_inverting                  = false
+y_min_endstop_inverting                  = false
+z_min_endstop_inverting                  = false
+
+default_axis_steps_per_unit              = { 80, 80, 400, 500 }
+axis_relative_modes                      = { false, false, false, false }
+default_max_feedrate                     = { 300, 300, 5, 25 }
+default_max_acceleration                 = { 3000, 3000, 100, 10000 }
+
+homing_feedrate_mm_m                     = { (50*60), (50*60), (4*60) }
+homing_bump_divisor                      = { 2, 2, 4 }
+
+x_enable_on                              = 0
+y_enable_on                              = 0
+z_enable_on                              = 0
+e_enable_on                              = 0
+
+invert_x_dir                             = false
+invert_y_dir                             = true
+invert_z_dir                             = false
+invert_e0_dir                            = false
+
+invert_e_step_pin                        = false
+invert_x_step_pin                        = false
+invert_y_step_pin                        = false
+invert_z_step_pin                        = false
+
+disable_x                                = false
+disable_y                                = false
+disable_z                                = false
+disable_e                                = false
+
+proportional_font_ratio                  = 1.0
+default_nominal_filament_dia             = 1.75
+
+junction_deviation_mm                    = 0.013
+
+default_acceleration                     = 3000
+default_travel_acceleration              = 3000
+default_retract_acceleration             = 3000
+
+default_minimumfeedrate                  = 0.0
+default_mintravelfeedrate                = 0.0
+
+minimum_planner_speed                    = 0.05
+min_steps_per_segment                    = 6
+default_minsegmenttime                   = 20000
+
+[config:basic]
+bed_overshoot                            = 10
+busy_while_heating                       = on
+default_ejerk                            = 5.0
+default_keepalive_interval               = 2
+default_leveling_fade_height             = 0.0
+disable_inactive_extruder                = on
+display_charset_hd44780                  = JAPANESE
+eeprom_boot_silent                       = on
+eeprom_chitchat                          = on
+endstoppullups                           = on
+extrude_maxlength                        = 200
+extrude_mintemp                          = 170
+host_keepalive_feature                   = on
+hotend_overshoot                         = 15
+jd_handle_small_segments                 = on
+lcd_info_screen_style                    = 0
+lcd_language                             = en
+max_bed_power                            = 255
+mesh_inset                               = 0
+min_software_endstops                    = on
+max_software_endstops                    = on
+min_software_endstop_x                   = on
+min_software_endstop_y                   = on
+min_software_endstop_z                   = on
+max_software_endstop_x                   = on
+max_software_endstop_y                   = on
+max_software_endstop_z                   = on
+preheat_1_fan_speed                      = 0
+preheat_1_label                          = "PLA"
+preheat_1_temp_bed                       = 70
+prevent_cold_extrusion                   = on
+prevent_lengthy_extrude                  = on
+printjob_timer_autostart                 = on
+probing_margin                           = 10
+show_bootscreen                          = on
+soft_pwm_scale                           = 0
+string_config_h_author                   = "(none, default config)"
+temp_bed_hysteresis                      = 3
+temp_bed_residency_time                  = 10
+temp_bed_window                          = 1
+temp_residency_time                      = 10
+temp_window                              = 1
+validate_homing_endstops                 = on
+xy_probe_feedrate                        = (133*60)
+z_clearance_between_probes               = 5
+z_clearance_deploy_probe                 = 10
+z_clearance_multi_probe                  = 5
+
+[config:advanced]
+arc_support                              = on
+auto_report_temperatures                 = on
+autotemp                                 = on
+autotemp_oldweight                       = 0.98
+bed_check_interval                       = 5000
+default_stepper_deactive_time            = 120
+default_volumetric_extruder_limit        = 0.00
+disable_inactive_e                       = true
+disable_inactive_x                       = true
+disable_inactive_y                       = true
+disable_inactive_z                       = true
+e0_auto_fan_pin                          = -1
+encoder_100x_steps_per_sec               = 80
+encoder_10x_steps_per_sec                = 30
+encoder_rate_multiplier                  = on
+extended_capabilities_report             = on
+extruder_auto_fan_speed                  = 255
+extruder_auto_fan_temperature            = 50
+fanmux0_pin                              = -1
+fanmux1_pin                              = -1
+fanmux2_pin                              = -1
+faster_gcode_parser                      = on
+homing_bump_mm                           = { 5, 5, 2 }
+max_arc_segment_mm                       = 1.0
+min_arc_segment_mm                       = 0.1
+min_circle_segments                      = 72
+n_arc_correction                         = 25
+serial_overrun_protection                = on
+slowdown                                 = on
+slowdown_divisor                         = 2
+temp_sensor_bed                          = 0
+thermal_protection_bed_hysteresis        = 2
+thermocouple_max_errors                  = 15
+tx_buffer_size                           = 0
+watch_bed_temp_increase                  = 2
+watch_bed_temp_period                    = 60
+watch_temp_increase                      = 2
+watch_temp_period                        = 20
diff --git a/Marlin/src/HAL/AVR/HAL.cpp b/Marlin/src/HAL/AVR/HAL.cpp
index 58d57c8ee5..5382eb36a2 100644
--- a/Marlin/src/HAL/AVR/HAL.cpp
+++ b/Marlin/src/HAL/AVR/HAL.cpp
@@ -23,18 +23,45 @@
 
 #include "../../inc/MarlinConfig.h"
 #include "HAL.h"
+#include 
+
+#ifdef USBCON
+  DefaultSerial1 MSerial0(false, Serial);
+  #ifdef BLUETOOTH
+    BTSerial btSerial(false, bluetoothSerial);
+  #endif
+#endif
 
 // ------------------------
 // Public Variables
 // ------------------------
 
-//uint8_t MCUSR;
+// Don't initialize/override variable (which would happen in .init4)
+uint8_t MarlinHAL::reset_reason __attribute__((section(".noinit")));
 
 // ------------------------
 // Public functions
 // ------------------------
 
-void HAL_init() {
+__attribute__((naked))             // Don't output function pro- and epilogue
+__attribute__((used))              // Output the function, even if "not used"
+__attribute__((section(".init3"))) // Put in an early user definable section
+void save_reset_reason() {
+  #if ENABLED(OPTIBOOT_RESET_REASON)
+    __asm__ __volatile__(
+      A("STS %0, r2")
+      : "=m"(hal.reset_reason)
+    );
+  #else
+    hal.reset_reason = MCUSR;
+  #endif
+
+  // Clear within 16ms since WDRF bit enables a 16ms watchdog timer -> Boot loop
+  hal.clear_reset_source();
+  wdt_disable();
+}
+
+void MarlinHAL::init() {
   // Init Servo Pins
   #define INIT_SERVO(N) OUT_WRITE(SERVO##N##_PIN, LOW)
   #if HAS_SERVO_0
@@ -49,8 +76,75 @@ void HAL_init() {
   #if HAS_SERVO_3
     INIT_SERVO(3);
   #endif
+
+  init_pwm_timers();   // Init user timers to default frequency - 1000HZ
 }
 
+void MarlinHAL::reboot() {
+  #if ENABLED(USE_WATCHDOG)
+    while (1) { /* run out the watchdog */ }
+  #else
+    void (*resetFunc)() = 0;  // Declare resetFunc() at address 0
+    resetFunc();              // Jump to address 0
+  #endif
+}
+
+// ------------------------
+// Watchdog Timer
+// ------------------------
+
+#if ENABLED(USE_WATCHDOG)
+
+  #include 
+  #include "../../MarlinCore.h"
+
+  // Initialize watchdog with 8s timeout, if possible. Otherwise, make it 4s.
+  void MarlinHAL::watchdog_init() {
+    #if ENABLED(WATCHDOG_DURATION_8S) && defined(WDTO_8S)
+      #define WDTO_NS WDTO_8S
+    #else
+      #define WDTO_NS WDTO_4S
+    #endif
+    #if ENABLED(WATCHDOG_RESET_MANUAL)
+      // Enable the watchdog timer, but only for the interrupt.
+      // Take care, as this requires the correct order of operation, with interrupts disabled.
+      // See the datasheet of any AVR chip for details.
+      wdt_reset();
+      cli();
+      _WD_CONTROL_REG = _BV(_WD_CHANGE_BIT) | _BV(WDE);
+      _WD_CONTROL_REG = _BV(WDIE) | (WDTO_NS & 0x07) | ((WDTO_NS & 0x08) << 2); // WDTO_NS directly does not work. bit 0-2 are consecutive in the register but the highest value bit is at bit 5
+                                                                                // So worked for up to WDTO_2S
+      sei();
+      wdt_reset();
+    #else
+      wdt_enable(WDTO_NS); // The function handles the upper bit correct.
+    #endif
+    //delay(10000); // test it!
+  }
+
+  //===========================================================================
+  //=================================== ISR ===================================
+  //===========================================================================
+
+  // Watchdog timer interrupt, called if main program blocks >4sec and manual reset is enabled.
+  #if ENABLED(WATCHDOG_RESET_MANUAL)
+    ISR(WDT_vect) {
+      sei();  // With the interrupt driven serial we need to allow interrupts.
+      SERIAL_ERROR_MSG(STR_WATCHDOG_FIRED);
+      minkill();  // interrupt-safe final kill and infinite loop
+    }
+  #endif
+
+  // Reset watchdog. MUST be called at least every 4 seconds after the
+  // first watchdog_init or AVR will go into emergency procedures.
+  void MarlinHAL::watchdog_refresh() { wdt_reset(); }
+
+#endif // USE_WATCHDOG
+
+// ------------------------
+// Free Memory Accessor
+// ------------------------
+
 #if ENABLED(SDSUPPORT)
 
   #include "../../sd/SdFatUtil.h"
@@ -58,20 +152,20 @@ void HAL_init() {
 
 #else // !SDSUPPORT
 
-extern "C" {
-  extern char __bss_end;
-  extern char __heap_start;
-  extern void* __brkval;
+  extern "C" {
+    extern char __bss_end;
+    extern char __heap_start;
+    extern void* __brkval;
 
-  int freeMemory() {
-    int free_memory;
-    if ((int)__brkval == 0)
-      free_memory = ((int)&free_memory) - ((int)&__bss_end);
-    else
-      free_memory = ((int)&free_memory) - ((int)__brkval);
-    return free_memory;
+    int freeMemory() {
+      int free_memory;
+      if ((int)__brkval == 0)
+        free_memory = ((int)&free_memory) - ((int)&__bss_end);
+      else
+        free_memory = ((int)&free_memory) - ((int)__brkval);
+      return free_memory;
+    }
   }
-}
 
 #endif // !SDSUPPORT
 
diff --git a/Marlin/src/HAL/AVR/HAL.h b/Marlin/src/HAL/AVR/HAL.h
index 6e0afa8f10..d458790979 100644
--- a/Marlin/src/HAL/AVR/HAL.h
+++ b/Marlin/src/HAL/AVR/HAL.h
@@ -19,17 +19,20 @@
  */
 #pragma once
 
+/**
+ * HAL for Arduino AVR
+ */
+
 #include "../shared/Marduino.h"
 #include "../shared/HAL_SPI.h"
 #include "fastio.h"
-#include "watchdog.h"
 #include "math.h"
 
-#ifdef IS_AT90USB
+#ifdef USBCON
   #include 
 #else
-  #define HardwareSerial_h // Hack to prevent HardwareSerial.h header inclusion
   #include "MarlinSerial.h"
+  #define BOARD_NO_NATIVE_USB
 #endif
 
 #include 
@@ -39,6 +42,19 @@
 #include 
 #include 
 
+//
+// Default graphical display delays
+//
+#if F_CPU >= 20000000
+  #define CPU_ST7920_DELAY_1 150
+  #define CPU_ST7920_DELAY_2   0
+  #define CPU_ST7920_DELAY_3 150
+#elif F_CPU == 16000000
+  #define CPU_ST7920_DELAY_1 125
+  #define CPU_ST7920_DELAY_2   0
+  #define CPU_ST7920_DELAY_3 188
+#endif
+
 #ifndef pgm_read_ptr
   // Compatibility for avr-libc 1.8.0-4.1 included with Ubuntu for
   // Windows Subsystem for Linux on Windows 10 as of 10/18/2019
@@ -61,9 +77,9 @@
   #define CRITICAL_SECTION_START()  unsigned char _sreg = SREG; cli()
   #define CRITICAL_SECTION_END()    SREG = _sreg
 #endif
-#define ISRS_ENABLED() TEST(SREG, SREG_I)
-#define ENABLE_ISRS()  sei()
-#define DISABLE_ISRS() cli()
+
+#define HAL_CAN_SET_PWM_FREQ   // This HAL supports PWM Frequency adjustment
+#define PWM_FREQUENCY 1000     // Default PWM frequency when set_pwm_duty() is called without set_pwm_frequency()
 
 // ------------------------
 // Types
@@ -71,35 +87,56 @@
 
 typedef int8_t pin_t;
 
-#define SHARED_SERVOS HAS_SERVOS
-#define HAL_SERVO_LIB Servo
+#define SHARED_SERVOS HAS_SERVOS  // Use shared/servos.cpp
+
+class Servo;
+typedef Servo hal_servo_t;
 
 // ------------------------
-// Public Variables
-// ------------------------
-
-//extern uint8_t MCUSR;
-
 // Serial ports
-#ifdef IS_AT90USB
-  #define MYSERIAL0 TERN(BLUETOOTH, bluetoothSerial, Serial)
-#else
-  #if !WITHIN(SERIAL_PORT, -1, 3)
-    #error "SERIAL_PORT must be from -1 to 3. Please update your configuration."
+// ------------------------
+
+#ifdef USBCON
+  #include "../../core/serial_hook.h"
+  typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1;
+  extern DefaultSerial1 MSerial0;
+  #ifdef BLUETOOTH
+    typedef ForwardSerial1Class< decltype(bluetoothSerial) > BTSerial;
+    extern BTSerial btSerial;
   #endif
-  #define MYSERIAL0 customizedSerial1
+
+  #define MYSERIAL1 TERN(BLUETOOTH, btSerial, MSerial0)
+#else
+  #if !WITHIN(SERIAL_PORT, 0, 3)
+    #error "SERIAL_PORT must be from 0 to 3."
+  #endif
+  #define MYSERIAL1 customizedSerial1
 
   #ifdef SERIAL_PORT_2
-    #if !WITHIN(SERIAL_PORT_2, -1, 3)
-      #error "SERIAL_PORT_2 must be from -1 to 3. Please update your configuration."
+    #if !WITHIN(SERIAL_PORT_2, 0, 3)
+      #error "SERIAL_PORT_2 must be from 0 to 3."
     #endif
-    #define MYSERIAL1 customizedSerial2
+    #define MYSERIAL2 customizedSerial2
+  #endif
+
+  #ifdef SERIAL_PORT_3
+    #if !WITHIN(SERIAL_PORT_3, 0, 3)
+      #error "SERIAL_PORT_3 must be from 0 to 3."
+    #endif
+    #define MYSERIAL3 customizedSerial3
   #endif
 #endif
 
+#ifdef MMU2_SERIAL_PORT
+  #if !WITHIN(MMU2_SERIAL_PORT, 0, 3)
+    #error "MMU2_SERIAL_PORT must be from 0 to 3"
+  #endif
+  #define MMU2_SERIAL mmuSerial
+#endif
+
 #ifdef LCD_SERIAL_PORT
-  #if !WITHIN(LCD_SERIAL_PORT, -1, 3)
-    #error "LCD_SERIAL_PORT must be from -1 to 3. Please update your configuration."
+  #if !WITHIN(LCD_SERIAL_PORT, 0, 3)
+    #error "LCD_SERIAL_PORT must be from 0 to 3."
   #endif
   #define LCD_SERIAL lcdSerial
   #if HAS_DGUS_LCD
@@ -107,60 +144,20 @@ typedef int8_t pin_t;
   #endif
 #endif
 
-// ------------------------
-// Public functions
-// ------------------------
-
-void HAL_init();
-
-//void cli();
-
-//void _delay_ms(const int delay);
-
-inline void HAL_clear_reset_source() { MCUSR = 0; }
-inline uint8_t HAL_get_reset_source() { return MCUSR; }
-
-inline void HAL_reboot() {}  // reboot the board or restart the bootloader
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-extern "C" {
-  int freeMemory();
-}
-#pragma GCC diagnostic pop
-
+//
 // ADC
-#ifdef DIDR2
-  #define HAL_ANALOG_SELECT(ind) do{ if (ind < 8) SBI(DIDR0, ind); else SBI(DIDR2, ind & 0x07); }while(0)
-#else
-  #define HAL_ANALOG_SELECT(ind) SBI(DIDR0, ind);
-#endif
-
-inline void HAL_adc_init() {
-  ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADIF) | 0x07;
-  DIDR0 = 0;
-  #ifdef DIDR2
-    DIDR2 = 0;
-  #endif
-}
-
-#define SET_ADMUX_ADCSRA(ch) ADMUX = _BV(REFS0) | (ch & 0x07); SBI(ADCSRA, ADSC)
-#ifdef MUX5
-  #define HAL_START_ADC(ch) if (ch > 7) ADCSRB = _BV(MUX5); else ADCSRB = 0; SET_ADMUX_ADCSRA(ch)
-#else
-  #define HAL_START_ADC(ch) ADCSRB = 0; SET_ADMUX_ADCSRA(ch)
-#endif
-
+//
 #define HAL_ADC_VREF        5.0
 #define HAL_ADC_RESOLUTION 10
-#define HAL_READ_ADC()  ADC
-#define HAL_ADC_READY() !TEST(ADCSRA, ADSC)
 
+//
+// Pin Mapping for M42, M43, M226
+//
 #define GET_PIN_MAP_PIN(index) index
 #define GET_PIN_MAP_INDEX(pin) pin
 #define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval)
 
-#define HAL_SENSITIVE_PINS 0, 1
+#define HAL_SENSITIVE_PINS 0, 1,
 
 #ifdef __AVR_AT90USB1286__
   #define JTAG_DISABLE() do{ MCUCR = 0x80; MCUCR = 0x80; }while(0)
@@ -169,23 +166,113 @@ inline void HAL_adc_init() {
 // AVR compatibility
 #define strtof strtod
 
-#define HAL_CAN_SET_PWM_FREQ   // This HAL supports PWM Frequency adjustment
+// ------------------------
+// Free Memory Accessor
+// ------------------------
 
-/**
- *  set_pwm_frequency
- *  Sets the frequency of the timer corresponding to the provided pin
- *  as close as possible to the provided desired frequency. Internally
- *  calculates the required waveform generation mode, prescaler and
- *  resolution values required and sets the timer registers accordingly.
- *  NOTE that the frequency is applied to all pins on the timer (Ex OC3A, OC3B and OC3B)
- *  NOTE that there are limitations, particularly if using TIMER2. (see Configuration_adv.h -> FAST FAN PWM Settings)
- */
-void set_pwm_frequency(const pin_t pin, int f_desired);
+#pragma GCC diagnostic push
+#if GCC_VERSION <= 50000
+  #pragma GCC diagnostic ignored "-Wunused-function"
+#endif
 
-/**
- * set_pwm_duty
- *  Sets the PWM duty cycle of the provided pin to the provided value
- *  Optionally allows inverting the duty cycle [default = false]
- *  Optionally allows changing the maximum size of the provided value to enable finer PWM duty control [default = 255]
- */
-void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);
+extern "C" int freeMemory();
+
+#pragma GCC diagnostic pop
+
+// ------------------------
+// MarlinHAL Class
+// ------------------------
+
+class MarlinHAL {
+public:
+
+  // Earliest possible init, before setup()
+  MarlinHAL() {}
+
+  // Watchdog
+  static void watchdog_init()    IF_DISABLED(USE_WATCHDOG, {});
+  static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {});
+
+  static void init();          // Called early in setup()
+  static void init_board() {}  // Called less early in setup()
+  static void reboot();        // Restart the firmware from 0x0
+
+  // Interrupts
+  static bool isr_state() { return TEST(SREG, SREG_I); }
+  static void isr_on()  { sei(); }
+  static void isr_off() { cli(); }
+
+  static void delay_ms(const int ms) { _delay_ms(ms); }
+
+  // Tasks, called from idle()
+  static void idletask() {}
+
+  // Reset
+  static uint8_t reset_reason;
+  static uint8_t get_reset_source() { return reset_reason; }
+  static void clear_reset_source() { MCUSR = 0; }
+
+  // Free SRAM
+  static int freeMemory() { return ::freeMemory(); }
+
+  //
+  // ADC Methods
+  //
+
+  // Called by Temperature::init once at startup
+  static void adc_init() {
+    ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADIF) | 0x07;
+    DIDR0 = 0;
+    #ifdef DIDR2
+      DIDR2 = 0;
+    #endif
+  }
+
+  // Called by Temperature::init for each sensor at startup
+  static void adc_enable(const uint8_t ch) {
+    #ifdef DIDR2
+      if (ch > 7) { SBI(DIDR2, ch & 0x07); return; }
+    #endif
+    SBI(DIDR0, ch);
+  }
+
+  // Begin ADC sampling on the given channel. Called from Temperature::isr!
+  static void adc_start(const uint8_t ch) {
+    #ifdef MUX5
+      ADCSRB = ch > 7 ? _BV(MUX5) : 0;
+    #else
+      ADCSRB = 0;
+    #endif
+    ADMUX = _BV(REFS0) | (ch & 0x07);
+    SBI(ADCSRA, ADSC);
+  }
+
+  // Is the ADC ready for reading?
+  static bool adc_ready() { return !TEST(ADCSRA, ADSC); }
+
+  // The current value of the ADC register
+  static __typeof__(ADC) adc_value() { return ADC; }
+
+  /**
+   * init_pwm_timers
+   * Set the default frequency for timers 2-5 to 1000HZ
+   */
+  static void init_pwm_timers();
+
+  /**
+   * Set the PWM duty cycle for the pin to the given value.
+   * Optionally invert the duty cycle [default = false]
+   * Optionally change the scale of the provided value to enable finer PWM duty control [default = 255]
+   */
+  static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);
+
+  /**
+   * Set the frequency of the timer for the given pin as close as
+   * possible to the provided desired frequency. Internally calculate
+   * the required waveform generation mode, prescaler, and resolution
+   * values and set timer registers accordingly.
+   * NOTE that the frequency is applied to all pins on the timer (Ex OC3A, OC3B and OC3B)
+   * NOTE that there are limitations, particularly if using TIMER2. (see Configuration_adv.h -> FAST_PWM_FAN Settings)
+   */
+  static void set_pwm_frequency(const pin_t pin, const uint16_t f_desired);
+};
diff --git a/Marlin/src/HAL/AVR/HAL_SPI.cpp b/Marlin/src/HAL/AVR/HAL_SPI.cpp
index 31e589746c..dc98f2f79e 100644
--- a/Marlin/src/HAL/AVR/HAL_SPI.cpp
+++ b/Marlin/src/HAL/AVR/HAL_SPI.cpp
@@ -34,21 +34,21 @@
 #include "../../inc/MarlinConfig.h"
 
 void spiBegin() {
-  OUT_WRITE(SS_PIN, HIGH);
-  SET_OUTPUT(SCK_PIN);
-  SET_INPUT(MISO_PIN);
-  SET_OUTPUT(MOSI_PIN);
-
-  #if DISABLED(SOFTWARE_SPI)
-    // SS must be in output mode even it is not chip select
-    //SET_OUTPUT(SS_PIN);
-    // set SS high - may be chip select for another SPI device
-    //#if SET_SPI_SS_HIGH
-      //WRITE(SS_PIN, HIGH);
-    //#endif
-    // set a default rate
-    spiInit(1);
+  #if PIN_EXISTS(SD_SS)
+    // Do not init HIGH for boards with pin 4 used as Fans or Heaters or otherwise, not likely to have multiple SPI devices anyway.
+    #if defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || defined(__AVR_ATmega1284P__)
+      // SS must be in output mode even it is not chip select
+      SET_OUTPUT(SD_SS_PIN);
+    #else
+      // set SS high - may be chip select for another SPI device
+      OUT_WRITE(SD_SS_PIN, HIGH);
+    #endif
   #endif
+  SET_OUTPUT(SD_SCK_PIN);
+  SET_INPUT(SD_MISO_PIN);
+  SET_OUTPUT(SD_MOSI_PIN);
+
+  IF_DISABLED(SOFTWARE_SPI, spiInit(SPI_HALF_SPEED));
 }
 
 #if NONE(SOFTWARE_SPI, FORCE_SOFT_SPI)
@@ -74,7 +74,8 @@ void spiBegin() {
       #elif defined(PRR0)
         PRR0
       #endif
-        , PRSPI);
+      , PRSPI
+    );
 
     SPCR = _BV(SPE) | _BV(MSTR) | (spiRate >> 1);
     SPSR = spiRate & 1 || spiRate == 6 ? 0 : _BV(SPI2X);
@@ -88,7 +89,7 @@ void spiBegin() {
   }
 
   /** SPI read data  */
-  void spiRead(uint8_t* buf, uint16_t nbyte) {
+  void spiRead(uint8_t *buf, uint16_t nbyte) {
     if (nbyte-- == 0) return;
     SPDR = 0xFF;
     for (uint16_t i = 0; i < nbyte; i++) {
@@ -107,7 +108,7 @@ void spiBegin() {
   }
 
   /** SPI send block  */
-  void spiSendBlock(uint8_t token, const uint8_t* buf) {
+  void spiSendBlock(uint8_t token, const uint8_t *buf) {
     SPDR = token;
     for (uint16_t i = 0; i < 512; i += 2) {
       while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ }
@@ -195,19 +196,19 @@ void spiBegin() {
     // no interrupts during byte receive - about 8µs
     cli();
     // output pin high - like sending 0xFF
-    WRITE(MOSI_PIN, HIGH);
+    WRITE(SD_MOSI_PIN, HIGH);
 
     LOOP_L_N(i, 8) {
-      WRITE(SCK_PIN, HIGH);
+      WRITE(SD_SCK_PIN, HIGH);
 
       nop; // adjust so SCK is nice
       nop;
 
       data <<= 1;
 
-      if (READ(MISO_PIN)) data |= 1;
+      if (READ(SD_MISO_PIN)) data |= 1;
 
-      WRITE(SCK_PIN, LOW);
+      WRITE(SD_SCK_PIN, LOW);
     }
 
     sei();
@@ -215,7 +216,7 @@ void spiBegin() {
   }
 
   // Soft SPI read data
-  void spiRead(uint8_t* buf, uint16_t nbyte) {
+  void spiRead(uint8_t *buf, uint16_t nbyte) {
     for (uint16_t i = 0; i < nbyte; i++)
       buf[i] = spiRec();
   }
@@ -225,10 +226,10 @@ void spiBegin() {
     // no interrupts during byte send - about 8µs
     cli();
     LOOP_L_N(i, 8) {
-      WRITE(SCK_PIN, LOW);
-      WRITE(MOSI_PIN, data & 0x80);
+      WRITE(SD_SCK_PIN, LOW);
+      WRITE(SD_MOSI_PIN, data & 0x80);
       data <<= 1;
-      WRITE(SCK_PIN, HIGH);
+      WRITE(SD_SCK_PIN, HIGH);
     }
 
     nop; // hold SCK high for a few ns
@@ -236,13 +237,13 @@ void spiBegin() {
     nop;
     nop;
 
-    WRITE(SCK_PIN, LOW);
+    WRITE(SD_SCK_PIN, LOW);
 
     sei();
   }
 
   // Soft SPI send block
-  void spiSendBlock(uint8_t token, const uint8_t* buf) {
+  void spiSendBlock(uint8_t token, const uint8_t *buf) {
     spiSend(token);
     for (uint16_t i = 0; i < 512; i++)
       spiSend(buf[i]);
diff --git a/Marlin/src/HAL/STM32/watchdog.h b/Marlin/src/HAL/AVR/MarlinSPI.h
similarity index 87%
rename from Marlin/src/HAL/STM32/watchdog.h
rename to Marlin/src/HAL/AVR/MarlinSPI.h
index 49a0d9c631..0c447ba4cb 100644
--- a/Marlin/src/HAL/STM32/watchdog.h
+++ b/Marlin/src/HAL/AVR/MarlinSPI.h
@@ -1,6 +1,6 @@
 /**
  * Marlin 3D Printer Firmware
- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  *
  * Based on Sprinter and grbl.
  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
@@ -21,5 +21,6 @@
  */
 #pragma once
 
-void watchdog_init();
-void HAL_watchdog_refresh();
+#include 
+
+using MarlinSPI = SPIClass;
diff --git a/Marlin/src/HAL/AVR/MarlinSerial.cpp b/Marlin/src/HAL/AVR/MarlinSerial.cpp
index 8feac32aa7..986462437c 100644
--- a/Marlin/src/HAL/AVR/MarlinSerial.cpp
+++ b/Marlin/src/HAL/AVR/MarlinSerial.cpp
@@ -38,7 +38,7 @@
 
 #include "../../inc/MarlinConfig.h"
 
-#if !IS_AT90USB && (defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H))
+#if !defined(USBCON) && (defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H))
 
 #include "MarlinSerial.h"
 #include "../../MarlinCore.h"
@@ -486,7 +486,7 @@ void MarlinSerial::write(const uint8_t c) {
     const uint8_t i = (tx_buffer.head + 1) & (Cfg::TX_SIZE - 1);
 
     // If global interrupts are disabled (as the result of being called from an ISR)...
-    if (!ISRS_ENABLED()) {
+    if (!hal.isr_state()) {
 
       // Make room by polling if it is possible to transmit, and do so!
       while (i == tx_buffer.tail) {
@@ -534,7 +534,7 @@ void MarlinSerial::flushTX() {
     if (!_written) return;
 
     // If global interrupts are disabled (as the result of being called from an ISR)...
-    if (!ISRS_ENABLED()) {
+    if (!hal.isr_state()) {
 
       // Wait until everything was transmitted - We must do polling, as interrupts are disabled
       while (tx_buffer.head != tx_buffer.tail || !B_TXC) {
@@ -556,161 +556,6 @@ void MarlinSerial::flushTX() {
   }
 }
 
-/**
- * Imports from print.h
- */
-
-template
-void MarlinSerial::print(char c, int base) {
-  print((long)c, base);
-}
-
-template
-void MarlinSerial::print(unsigned char b, int base) {
-  print((unsigned long)b, base);
-}
-
-template
-void MarlinSerial::print(int n, int base) {
-  print((long)n, base);
-}
-
-template
-void MarlinSerial::print(unsigned int n, int base) {
-  print((unsigned long)n, base);
-}
-
-template
-void MarlinSerial::print(long n, int base) {
-  if (base == 0) write(n);
-  else if (base == 10) {
-    if (n < 0) { print('-'); n = -n; }
-    printNumber(n, 10);
-  }
-  else
-    printNumber(n, base);
-}
-
-template
-void MarlinSerial::print(unsigned long n, int base) {
-  if (base == 0) write(n);
-  else printNumber(n, base);
-}
-
-template
-void MarlinSerial::print(double n, int digits) {
-  printFloat(n, digits);
-}
-
-template
-void MarlinSerial::println() {
-  print('\r');
-  print('\n');
-}
-
-template
-void MarlinSerial::println(const String& s) {
-  print(s);
-  println();
-}
-
-template
-void MarlinSerial::println(const char c[]) {
-  print(c);
-  println();
-}
-
-template
-void MarlinSerial::println(char c, int base) {
-  print(c, base);
-  println();
-}
-
-template
-void MarlinSerial::println(unsigned char b, int base) {
-  print(b, base);
-  println();
-}
-
-template
-void MarlinSerial::println(int n, int base) {
-  print(n, base);
-  println();
-}
-
-template
-void MarlinSerial::println(unsigned int n, int base) {
-  print(n, base);
-  println();
-}
-
-template
-void MarlinSerial::println(long n, int base) {
-  print(n, base);
-  println();
-}
-
-template
-void MarlinSerial::println(unsigned long n, int base) {
-  print(n, base);
-  println();
-}
-
-template
-void MarlinSerial::println(double n, int digits) {
-  print(n, digits);
-  println();
-}
-
-// Private Methods
-
-template
-void MarlinSerial::printNumber(unsigned long n, uint8_t base) {
-  if (n) {
-    unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
-    int8_t i = 0;
-    while (n) {
-      buf[i++] = n % base;
-      n /= base;
-    }
-    while (i--)
-      print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10)));
-  }
-  else
-    print('0');
-}
-
-template
-void MarlinSerial::printFloat(double number, uint8_t digits) {
-  // Handle negative numbers
-  if (number < 0.0) {
-    print('-');
-    number = -number;
-  }
-
-  // Round correctly so that print(1.999, 2) prints as "2.00"
-  double rounding = 0.5;
-  LOOP_L_N(i, digits) rounding *= 0.1;
-  number += rounding;
-
-  // Extract the integer part of the number and print it
-  unsigned long int_part = (unsigned long)number;
-  double remainder = number - (double)int_part;
-  print(int_part);
-
-  // Print the decimal point, but only if there are digits beyond
-  if (digits) {
-    print('.');
-    // Extract digits from the remainder one at a time
-    while (digits--) {
-      remainder *= 10.0;
-      int toPrint = int(remainder);
-      print(toPrint);
-      remainder -= toPrint;
-    }
-  }
-}
-
 // Hookup ISR handlers
 ISR(SERIAL_REGNAME(USART, SERIAL_PORT, _RX_vect)) {
   MarlinSerial>::store_rxd_char();
@@ -720,11 +565,9 @@ ISR(SERIAL_REGNAME(USART, SERIAL_PORT, _UDRE_vect)) {
   MarlinSerial>::_tx_udr_empty_irq();
 }
 
-// Preinstantiate
-template class MarlinSerial>;
-
-// Instantiate
-MarlinSerial> customizedSerial1;
+// Because of the template definition above, it's required to instantiate the template to have all methods generated
+template class MarlinSerial< MarlinSerialCfg >;
+MSerialT1 customizedSerial1(MSerialT1::HasEmergencyParser);
 
 #ifdef SERIAL_PORT_2
 
@@ -737,13 +580,26 @@ MarlinSerial> customizedSerial1;
     MarlinSerial>::_tx_udr_empty_irq();
   }
 
-  // Preinstantiate
-  template class MarlinSerial>;
+  template class MarlinSerial< MarlinSerialCfg >;
+  MSerialT2 customizedSerial2(MSerialT2::HasEmergencyParser);
 
-  // Instantiate
-  MarlinSerial> customizedSerial2;
+#endif // SERIAL_PORT_2
 
-#endif
+#ifdef SERIAL_PORT_3
+
+  // Hookup ISR handlers
+  ISR(SERIAL_REGNAME(USART, SERIAL_PORT_3, _RX_vect)) {
+    MarlinSerial>::store_rxd_char();
+  }
+
+  ISR(SERIAL_REGNAME(USART, SERIAL_PORT_3, _UDRE_vect)) {
+    MarlinSerial>::_tx_udr_empty_irq();
+  }
+
+  template class MarlinSerial< MarlinSerialCfg >;
+  MSerialT3 customizedSerial3(MSerialT3::HasEmergencyParser);
+
+#endif // SERIAL_PORT_3
 
 #ifdef MMU2_SERIAL_PORT
 
@@ -755,13 +611,10 @@ MarlinSerial> customizedSerial1;
     MarlinSerial>::_tx_udr_empty_irq();
   }
 
-  // Preinstantiate
-  template class MarlinSerial>;
+  template class MarlinSerial< MMU2SerialCfg >;
+  MSerialMMU2 mmuSerial(MSerialMMU2::HasEmergencyParser);
 
-  // Instantiate
-  MarlinSerial> mmuSerial;
-
-#endif
+#endif // MMU2_SERIAL_PORT
 
 #ifdef LCD_SERIAL_PORT
 
@@ -773,11 +626,8 @@ MarlinSerial> customizedSerial1;
     MarlinSerial>::_tx_udr_empty_irq();
   }
 
-  // Preinstantiate
-  template class MarlinSerial>;
-
-  // Instantiate
-  MarlinSerial> lcdSerial;
+  template class MarlinSerial< LCDSerialCfg >;
+  MSerialLCD lcdSerial(MSerialLCD::HasEmergencyParser);
 
   #if HAS_DGUS_LCD
     template
@@ -790,13 +640,13 @@ MarlinSerial> customizedSerial1;
     }
   #endif
 
-#endif
+#endif // LCD_SERIAL_PORT
 
-#endif // !IS_AT90USB && (UBRRH || UBRR0H || UBRR1H || UBRR2H || UBRR3H)
+#endif // !USBCON && (UBRRH || UBRR0H || UBRR1H || UBRR2H || UBRR3H)
 
 // For AT90USB targets use the UART for BT interfacing
-#if BOTH(IS_AT90USB, BLUETOOTH)
-  HardwareSerial bluetoothSerial;
+#if defined(USBCON) && ENABLED(BLUETOOTH)
+  MSerialBT bluetoothSerial(false);
 #endif
 
 #endif // __AVR__
diff --git a/Marlin/src/HAL/AVR/MarlinSerial.h b/Marlin/src/HAL/AVR/MarlinSerial.h
index 3850e2a47e..7eb76000d6 100644
--- a/Marlin/src/HAL/AVR/MarlinSerial.h
+++ b/Marlin/src/HAL/AVR/MarlinSerial.h
@@ -34,6 +34,7 @@
 #include 
 
 #include "../../inc/MarlinConfigPre.h"
+#include "../../core/serial_hook.h"
 
 #ifndef SERIAL_PORT
   #define SERIAL_PORT 0
@@ -135,10 +136,6 @@
     UART_DECL(3);
   #endif
 
-  #define DEC 10
-  #define HEX 16
-  #define OCT 8
-  #define BIN 2
   #define BYTE 0
 
   // Templated type selector
@@ -194,68 +191,38 @@
                    rx_framing_errors;
     static ring_buffer_pos_t rx_max_enqueued;
 
-    static FORCE_INLINE ring_buffer_pos_t atomic_read_rx_head();
+    FORCE_INLINE static ring_buffer_pos_t atomic_read_rx_head();
 
     static volatile bool rx_tail_value_not_stable;
     static volatile uint16_t rx_tail_value_backup;
 
-    static FORCE_INLINE void atomic_set_rx_tail(ring_buffer_pos_t value);
-    static FORCE_INLINE ring_buffer_pos_t atomic_read_rx_tail();
-
-    public:
+    FORCE_INLINE static void atomic_set_rx_tail(ring_buffer_pos_t value);
+    FORCE_INLINE static ring_buffer_pos_t atomic_read_rx_tail();
 
+  public:
     FORCE_INLINE static void store_rxd_char();
     FORCE_INLINE static void _tx_udr_empty_irq();
 
-    public:
-      MarlinSerial() {};
-      static void begin(const long);
-      static void end();
-      static int peek();
-      static int read();
-      static void flush();
-      static ring_buffer_pos_t available();
-      static void write(const uint8_t c);
-      static void flushTX();
-      #if HAS_DGUS_LCD
-        static ring_buffer_pos_t get_tx_buffer_free();
-      #endif
+  public:
+    static void begin(const long);
+    static void end();
+    static int peek();
+    static int read();
+    static void flush();
+    static ring_buffer_pos_t available();
+    static void write(const uint8_t c);
+    static void flushTX();
+    #if HAS_DGUS_LCD
+      static ring_buffer_pos_t get_tx_buffer_free();
+    #endif
 
-      static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; }
+    enum { HasEmergencyParser = Cfg::EMERGENCYPARSER };
+    static bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; }
 
-      FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; }
-      FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; }
-      FORCE_INLINE static uint8_t framing_errors() { return Cfg::RX_FRAMING_ERRORS ? rx_framing_errors : 0; }
-      FORCE_INLINE static ring_buffer_pos_t rxMaxEnqueued() { return Cfg::MAX_RX_QUEUED ? rx_max_enqueued : 0; }
-
-      FORCE_INLINE static void write(const char* str) { while (*str) write(*str++); }
-      FORCE_INLINE static void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); }
-      FORCE_INLINE static void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); }
-      FORCE_INLINE static void print(const char* str) { write(str); }
-
-      static void print(char, int = BYTE);
-      static void print(unsigned char, int = BYTE);
-      static void print(int, int = DEC);
-      static void print(unsigned int, int = DEC);
-      static void print(long, int = DEC);
-      static void print(unsigned long, int = DEC);
-      static void print(double, int = 2);
-
-      static void println(const String& s);
-      static void println(const char[]);
-      static void println(char, int = BYTE);
-      static void println(unsigned char, int = BYTE);
-      static void println(int, int = DEC);
-      static void println(unsigned int, int = DEC);
-      static void println(long, int = DEC);
-      static void println(unsigned long, int = DEC);
-      static void println(double, int = 2);
-      static void println();
-      operator bool() { return true; }
-
-    private:
-      static void printNumber(unsigned long, const uint8_t);
-      static void printFloat(double, uint8_t);
+    FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; }
+    FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; }
+    FORCE_INLINE static uint8_t framing_errors() { return Cfg::RX_FRAMING_ERRORS ? rx_framing_errors : 0; }
+    FORCE_INLINE static ring_buffer_pos_t rxMaxEnqueued() { return Cfg::MAX_RX_QUEUED ? rx_max_enqueued : 0; }
   };
 
   template 
@@ -270,12 +237,18 @@
     static constexpr bool RX_FRAMING_ERRORS = ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS);
     static constexpr bool MAX_RX_QUEUED     = ENABLED(SERIAL_STATS_MAX_RX_QUEUED);
   };
-  extern MarlinSerial> customizedSerial1;
+
+  typedef Serial1Class< MarlinSerial< MarlinSerialCfg > > MSerialT1;
+  extern MSerialT1 customizedSerial1;
 
   #ifdef SERIAL_PORT_2
+    typedef Serial1Class< MarlinSerial< MarlinSerialCfg > > MSerialT2;
+    extern MSerialT2 customizedSerial2;
+  #endif
 
-    extern MarlinSerial> customizedSerial2;
-
+  #ifdef SERIAL_PORT_3
+    typedef Serial1Class< MarlinSerial< MarlinSerialCfg > > MSerialT3;
+    extern MSerialT3 customizedSerial3;
   #endif
 
 #endif // !USBCON
@@ -284,49 +257,41 @@
   template 
   struct MMU2SerialCfg {
     static constexpr int PORT               = serial;
+    static constexpr unsigned int RX_SIZE   = 32;
+    static constexpr unsigned int TX_SIZE   = 32;
     static constexpr bool XONOFF            = false;
     static constexpr bool EMERGENCYPARSER   = false;
     static constexpr bool DROPPED_RX        = false;
     static constexpr bool RX_FRAMING_ERRORS = false;
     static constexpr bool MAX_RX_QUEUED     = false;
-    static constexpr unsigned int RX_SIZE   = 32;
-    static constexpr unsigned int TX_SIZE   = 32;
     static constexpr bool RX_OVERRUNS       = false;
   };
 
-  extern MarlinSerial> mmuSerial;
+  typedef Serial1Class< MarlinSerial< MMU2SerialCfg > > MSerialMMU2;
+  extern MSerialMMU2 mmuSerial;
 #endif
 
 #ifdef LCD_SERIAL_PORT
 
   template 
   struct LCDSerialCfg {
-    static constexpr int PORT                 = serial;
-    static constexpr bool XONOFF              = false;
-    static constexpr bool EMERGENCYPARSER     = ENABLED(EMERGENCY_PARSER);
-    static constexpr bool DROPPED_RX          = false;
-    static constexpr bool RX_FRAMING_ERRORS   = false;
-    static constexpr bool MAX_RX_QUEUED       = false;
-    #if HAS_DGUS_LCD
-      static constexpr unsigned int RX_SIZE   = DGUS_RX_BUFFER_SIZE;
-      static constexpr unsigned int TX_SIZE   = DGUS_TX_BUFFER_SIZE;
-      static constexpr bool RX_OVERRUNS       = ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS);
-    #elif EITHER(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON)
-      static constexpr unsigned int RX_SIZE   = 64;
-      static constexpr unsigned int TX_SIZE   = 128;
-      static constexpr bool RX_OVERRUNS       = false;
-    #else
-      static constexpr unsigned int RX_SIZE   = 64;
-      static constexpr unsigned int TX_SIZE   = 128;
-      static constexpr bool RX_OVERRUNS       = false
-    #endif
+    static constexpr int PORT               = serial;
+    static constexpr unsigned int RX_SIZE   = TERN(HAS_DGUS_LCD, DGUS_RX_BUFFER_SIZE,  64);
+    static constexpr unsigned int TX_SIZE   = TERN(HAS_DGUS_LCD, DGUS_TX_BUFFER_SIZE, 128);
+    static constexpr bool XONOFF            = false;
+    static constexpr bool EMERGENCYPARSER   = ENABLED(EMERGENCY_PARSER);
+    static constexpr bool DROPPED_RX        = false;
+    static constexpr bool RX_FRAMING_ERRORS = false;
+    static constexpr bool MAX_RX_QUEUED     = false;
+    static constexpr bool RX_OVERRUNS       = BOTH(HAS_DGUS_LCD, SERIAL_STATS_RX_BUFFER_OVERRUNS);
   };
 
-  extern MarlinSerial> lcdSerial;
-
+  typedef Serial1Class< MarlinSerial< LCDSerialCfg > > MSerialLCD;
+  extern MSerialLCD lcdSerial;
 #endif
 
 // Use the UART for Bluetooth in AT90USB configurations
-#if BOTH(IS_AT90USB, BLUETOOTH)
-  extern HardwareSerial bluetoothSerial;
+#if defined(USBCON) && ENABLED(BLUETOOTH)
+  typedef Serial1Class MSerialBT;
+  extern MSerialBT bluetoothSerial;
 #endif
diff --git a/Marlin/src/HAL/AVR/Servo.cpp b/Marlin/src/HAL/AVR/Servo.cpp
index 526352b773..0a1ef5337a 100644
--- a/Marlin/src/HAL/AVR/Servo.cpp
+++ b/Marlin/src/HAL/AVR/Servo.cpp
@@ -66,27 +66,26 @@ static volatile int8_t Channel[_Nbr_16timers];              // counter for the s
 
 /************ static functions common to all instances ***********************/
 
-static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t* TCNTn, volatile uint16_t* OCRnA) {
-  if (Channel[timer] < 0)
-    *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
-  else {
-    if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && SERVO(timer, Channel[timer]).Pin.isActive)
-      extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, LOW); // pulse this channel low if activated
-  }
+static inline void handle_interrupts(const timer16_Sequence_t timer, volatile uint16_t* TCNTn, volatile uint16_t* OCRnA) {
+  int8_t cho = Channel[timer];                                        // Handle the prior Channel[timer] first
+  if (cho < 0)                                                        // Channel -1 indicates the refresh interval completed...
+    *TCNTn = 0;                                                       // ...so reset the timer
+  else if (SERVO_INDEX(timer, cho) < ServoCount)                      // prior channel handled?
+    extDigitalWrite(SERVO(timer, cho).Pin.nbr, LOW);                  // pulse the prior channel LOW
 
-  Channel[timer]++;    // increment to the next channel
-  if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
-    *OCRnA = *TCNTn + SERVO(timer, Channel[timer]).ticks;
-    if (SERVO(timer, Channel[timer]).Pin.isActive)    // check if activated
-      extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, HIGH); // it's an active channel so pulse it high
+  Channel[timer] = ++cho;                                             // Handle the next channel (or 0)
+  if (cho < SERVOS_PER_TIMER && SERVO_INDEX(timer, cho) < ServoCount) {
+    *OCRnA = *TCNTn + SERVO(timer, cho).ticks;                        // set compare to current ticks plus duration
+    if (SERVO(timer, cho).Pin.isActive)                               // activated?
+      extDigitalWrite(SERVO(timer, cho).Pin.nbr, HIGH);               // yes: pulse HIGH
   }
   else {
     // finished all channels so wait for the refresh period to expire before starting over
-    if (((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL))    // allow a few ticks to ensure the next OCR1A not missed
-      *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
-    else
-      *OCRnA = *TCNTn + 4;  // at least REFRESH_INTERVAL has elapsed
-    Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
+    const unsigned int cval = ((unsigned)*TCNTn) + 32 / (SERVO_TIMER_PRESCALER), // allow 32 cycles to ensure the next OCR1A not missed
+                       ival = (unsigned int)usToTicks(REFRESH_INTERVAL); // at least REFRESH_INTERVAL has elapsed
+    *OCRnA = max(cval, ival);
+
+    Channel[timer] = -1;                                              // reset the timer counter to 0 on the next call
   }
 }
 
@@ -123,91 +122,102 @@ static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t
 
 /****************** end of static functions ******************************/
 
-void initISR(timer16_Sequence_t timer) {
-  #ifdef _useTimer1
-    if (timer == _timer1) {
-      TCCR1A = 0;             // normal counting mode
-      TCCR1B = _BV(CS11);     // set prescaler of 8
-      TCNT1 = 0;              // clear the timer count
-      #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__)
-        SBI(TIFR, OCF1A);      // clear any pending interrupts;
-        SBI(TIMSK, OCIE1A);    // enable the output compare interrupt
-      #else
-        // here if not ATmega8 or ATmega128
-        SBI(TIFR1, OCF1A);     // clear any pending interrupts;
-        SBI(TIMSK1, OCIE1A);   // enable the output compare interrupt
-      #endif
-      #ifdef WIRING
-        timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service);
-      #endif
-    }
-  #endif
+void initISR(const timer16_Sequence_t timer_index) {
+  switch (timer_index) {
+    default: break;
 
-  #ifdef _useTimer3
-    if (timer == _timer3) {
-      TCCR3A = 0;             // normal counting mode
-      TCCR3B = _BV(CS31);     // set prescaler of 8
-      TCNT3 = 0;              // clear the timer count
-      #ifdef __AVR_ATmega128__
-        SBI(TIFR, OCF3A);     // clear any pending interrupts;
-        SBI(ETIMSK, OCIE3A);  // enable the output compare interrupt
-      #else
-        SBI(TIFR3, OCF3A);   // clear any pending interrupts;
-        SBI(TIMSK3, OCIE3A); // enable the output compare interrupt
-      #endif
-      #ifdef WIRING
-        timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service);  // for Wiring platform only
-      #endif
-    }
-  #endif
+    #ifdef _useTimer1
+      case _timer1:
+        TCCR1A = 0;             // normal counting mode
+        TCCR1B = _BV(CS11);     // set prescaler of 8
+        TCNT1 = 0;              // clear the timer count
+        #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__)
+          SBI(TIFR, OCF1A);      // clear any pending interrupts;
+          SBI(TIMSK, OCIE1A);    // enable the output compare interrupt
+        #else
+          // here if not ATmega8 or ATmega128
+          SBI(TIFR1, OCF1A);     // clear any pending interrupts;
+          SBI(TIMSK1, OCIE1A);   // enable the output compare interrupt
+        #endif
+        #ifdef WIRING
+          timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service);
+        #endif
+        break;
+    #endif
 
-  #ifdef _useTimer4
-    if (timer == _timer4) {
-      TCCR4A = 0;             // normal counting mode
-      TCCR4B = _BV(CS41);     // set prescaler of 8
-      TCNT4 = 0;              // clear the timer count
-      TIFR4 = _BV(OCF4A);     // clear any pending interrupts;
-      TIMSK4 = _BV(OCIE4A);   // enable the output compare interrupt
-    }
-  #endif
+    #ifdef _useTimer3
+      case _timer3:
+        TCCR3A = 0;             // normal counting mode
+        TCCR3B = _BV(CS31);     // set prescaler of 8
+        TCNT3 = 0;              // clear the timer count
+        #ifdef __AVR_ATmega128__
+          SBI(TIFR, OCF3A);     // clear any pending interrupts;
+          SBI(ETIMSK, OCIE3A);  // enable the output compare interrupt
+        #else
+          SBI(TIFR3, OCF3A);   // clear any pending interrupts;
+          SBI(TIMSK3, OCIE3A); // enable the output compare interrupt
+        #endif
+        #ifdef WIRING
+          timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service);  // for Wiring platform only
+        #endif
+        break;
+    #endif
 
-  #ifdef _useTimer5
-    if (timer == _timer5) {
-      TCCR5A = 0;             // normal counting mode
-      TCCR5B = _BV(CS51);     // set prescaler of 8
-      TCNT5 = 0;              // clear the timer count
-      TIFR5 = _BV(OCF5A);     // clear any pending interrupts;
-      TIMSK5 = _BV(OCIE5A);   // enable the output compare interrupt
-    }
-  #endif
+    #ifdef _useTimer4
+      case _timer4:
+        TCCR4A = 0;             // normal counting mode
+        TCCR4B = _BV(CS41);     // set prescaler of 8
+        TCNT4 = 0;              // clear the timer count
+        TIFR4 = _BV(OCF4A);     // clear any pending interrupts;
+        TIMSK4 = _BV(OCIE4A);   // enable the output compare interrupt
+        break;
+    #endif
+
+    #ifdef _useTimer5
+      case _timer5:
+        TCCR5A = 0;             // normal counting mode
+        TCCR5B = _BV(CS51);     // set prescaler of 8
+        TCNT5 = 0;              // clear the timer count
+        TIFR5 = _BV(OCF5A);     // clear any pending interrupts;
+        TIMSK5 = _BV(OCIE5A);   // enable the output compare interrupt
+        break;
+    #endif
+  }
 }
 
-void finISR(timer16_Sequence_t timer) {
+void finISR(const timer16_Sequence_t timer_index) {
   // Disable use of the given timer
   #ifdef WIRING
-    if (timer == _timer1) {
-      CBI(
-        #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
-          TIMSK1
-        #else
-          TIMSK
-        #endif
-          , OCIE1A);    // disable timer 1 output compare interrupt
-      timerDetach(TIMER1OUTCOMPAREA_INT);
-    }
-    else if (timer == _timer3) {
-      CBI(
-        #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
-          TIMSK3
-        #else
-          ETIMSK
-        #endif
-          , OCIE3A);    // disable the timer3 output compare A interrupt
-      timerDetach(TIMER3OUTCOMPAREA_INT);
+    switch (timer_index) {
+      default: break;
+
+      case _timer1:
+        CBI(
+          #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
+            TIMSK1
+          #else
+            TIMSK
+          #endif
+          , OCIE1A    // disable timer 1 output compare interrupt
+        );
+        timerDetach(TIMER1OUTCOMPAREA_INT);
+        break;
+
+      case _timer3:
+        CBI(
+          #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
+            TIMSK3
+          #else
+            ETIMSK
+          #endif
+          , OCIE3A    // disable the timer3 output compare A interrupt
+        );
+        timerDetach(TIMER3OUTCOMPAREA_INT);
+        break;
     }
   #else // !WIRING
     // For arduino - in future: call here to a currently undefined function to reset the timer
-    UNUSED(timer);
+    UNUSED(timer_index);
   #endif
 }
 
diff --git a/Marlin/src/HAL/AVR/eeprom.cpp b/Marlin/src/HAL/AVR/eeprom.cpp
index c7906985eb..8d084dec7f 100644
--- a/Marlin/src/HAL/AVR/eeprom.cpp
+++ b/Marlin/src/HAL/AVR/eeprom.cpp
@@ -40,13 +40,13 @@ bool PersistentStore::access_start()  { return true; }
 bool PersistentStore::access_finish() { return true; }
 
 bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+  uint16_t written = 0;
   while (size--) {
     uint8_t * const p = (uint8_t * const)pos;
     uint8_t v = *value;
-    // EEPROM has only ~100,000 write cycles,
-    // so only write bytes that have changed!
-    if (v != eeprom_read_byte(p)) {
+    if (v != eeprom_read_byte(p)) { // EEPROM has only ~100,000 write cycles, so only write bytes that have changed!
       eeprom_write_byte(p, v);
+      if (++written & 0x7F) delay(2); else safe_delay(2); // Avoid triggering watchdog during long EEPROM writes
       if (eeprom_read_byte(p) != v) {
         SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE);
         return true;
@@ -59,7 +59,7 @@ bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, ui
   return false;
 }
 
-bool PersistentStore::read_data(int &pos, uint8_t* value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
   do {
     uint8_t c = eeprom_read_byte((uint8_t*)pos);
     if (writing) *value = c;
diff --git a/Marlin/src/HAL/AVR/endstop_interrupts.h b/Marlin/src/HAL/AVR/endstop_interrupts.h
index ae9a605acc..5511aa406f 100644
--- a/Marlin/src/HAL/AVR/endstop_interrupts.h
+++ b/Marlin/src/HAL/AVR/endstop_interrupts.h
@@ -124,7 +124,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(X_MAX_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(X_MAX_PIN);
     #else
-      static_assert(digitalPinHasPCICR(X_MAX_PIN), "X_MAX_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(X_MAX_PIN), "X_MAX_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(X_MAX_PIN);
     #endif
   #endif
@@ -132,7 +132,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(X_MIN_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(X_MIN_PIN);
     #else
-      static_assert(digitalPinHasPCICR(X_MIN_PIN), "X_MIN_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(X_MIN_PIN), "X_MIN_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(X_MIN_PIN);
     #endif
   #endif
@@ -140,7 +140,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Y_MAX_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Y_MAX_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Y_MAX_PIN), "Y_MAX_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Y_MAX_PIN), "Y_MAX_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Y_MAX_PIN);
     #endif
   #endif
@@ -148,7 +148,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Y_MIN_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Y_MIN_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Y_MIN_PIN), "Y_MIN_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Y_MIN_PIN), "Y_MIN_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Y_MIN_PIN);
     #endif
   #endif
@@ -156,7 +156,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Z_MAX_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Z_MAX_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Z_MAX_PIN), "Z_MAX_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Z_MAX_PIN), "Z_MAX_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Z_MAX_PIN);
     #endif
   #endif
@@ -164,15 +164,105 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Z_MIN_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Z_MIN_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Z_MIN_PIN), "Z_MIN_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Z_MIN_PIN), "Z_MIN_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Z_MIN_PIN);
     #endif
   #endif
+  #if HAS_I_MAX
+    #if (digitalPinToInterrupt(I_MAX_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(I_MAX_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(I_MAX_PIN), "I_MAX_PIN is not interrupt-capable");
+      pciSetup(I_MAX_PIN);
+    #endif
+  #elif HAS_I_MIN
+    #if (digitalPinToInterrupt(I_MIN_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(I_MIN_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(I_MIN_PIN), "I_MIN_PIN is not interrupt-capable");
+      pciSetup(I_MIN_PIN);
+    #endif
+  #endif
+  #if HAS_J_MAX
+    #if (digitalPinToInterrupt(J_MAX_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(J_MAX_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(J_MAX_PIN), "J_MAX_PIN is not interrupt-capable");
+      pciSetup(J_MAX_PIN);
+    #endif
+  #elif HAS_J_MIN
+    #if (digitalPinToInterrupt(J_MIN_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(J_MIN_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(J_MIN_PIN), "J_MIN_PIN is not interrupt-capable");
+      pciSetup(J_MIN_PIN);
+    #endif
+  #endif
+  #if HAS_K_MAX
+    #if (digitalPinToInterrupt(K_MAX_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(K_MAX_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(K_MAX_PIN), "K_MAX_PIN is not interrupt-capable");
+      pciSetup(K_MAX_PIN);
+    #endif
+  #elif HAS_K_MIN
+    #if (digitalPinToInterrupt(K_MIN_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(K_MIN_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(K_MIN_PIN), "K_MIN_PIN is not interrupt-capable");
+      pciSetup(K_MIN_PIN);
+    #endif
+  #endif
+  #if HAS_U_MAX
+    #if (digitalPinToInterrupt(U_MAX_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(U_MAX_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(U_MAX_PIN), "U_MAX_PIN is not interrupt-capable");
+      pciSetup(U_MAX_PIN);
+    #endif
+  #elif HAS_U_MIN
+    #if (digitalPinToInterrupt(U_MIN_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(U_MIN_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(U_MIN_PIN), "U_MIN_PIN is not interrupt-capable");
+      pciSetup(U_MIN_PIN);
+    #endif
+  #endif
+  #if HAS_V_MAX
+    #if (digitalPinToInterrupt(V_MAX_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(V_MAX_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(V_MAX_PIN), "V_MAX_PIN is not interrupt-capable");
+      pciSetup(V_MAX_PIN);
+    #endif
+  #elif HAS_V_MIN
+    #if (digitalPinToInterrupt(V_MIN_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(V_MIN_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(V_MIN_PIN), "V_MIN_PIN is not interrupt-capable");
+      pciSetup(V_MIN_PIN);
+    #endif
+  #endif
+  #if HAS_W_MAX
+    #if (digitalPinToInterrupt(W_MAX_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(W_MAX_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(W_MAX_PIN), "W_MAX_PIN is not interrupt-capable");
+      pciSetup(W_MAX_PIN);
+    #endif
+  #elif HAS_W_MIN
+    #if (digitalPinToInterrupt(W_MIN_PIN) != NOT_AN_INTERRUPT)
+      _ATTACH(W_MIN_PIN);
+    #else
+      static_assert(digitalPinHasPCICR(W_MIN_PIN), "W_MIN_PIN is not interrupt-capable");
+      pciSetup(W_MIN_PIN);
+    #endif
+  #endif
   #if HAS_X2_MAX
     #if (digitalPinToInterrupt(X2_MAX_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(X2_MAX_PIN);
     #else
-      static_assert(digitalPinHasPCICR(X2_MAX_PIN), "X2_MAX_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(X2_MAX_PIN), "X2_MAX_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(X2_MAX_PIN);
     #endif
   #endif
@@ -180,7 +270,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(X2_MIN_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(X2_MIN_PIN);
     #else
-      static_assert(digitalPinHasPCICR(X2_MIN_PIN), "X2_MIN_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(X2_MIN_PIN), "X2_MIN_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(X2_MIN_PIN);
     #endif
   #endif
@@ -188,7 +278,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Y2_MAX_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Y2_MAX_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Y2_MAX_PIN), "Y2_MAX_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Y2_MAX_PIN), "Y2_MAX_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Y2_MAX_PIN);
     #endif
   #endif
@@ -196,7 +286,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Y2_MIN_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Y2_MIN_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Y2_MIN_PIN), "Y2_MIN_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Y2_MIN_PIN), "Y2_MIN_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Y2_MIN_PIN);
     #endif
   #endif
@@ -204,7 +294,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Z2_MAX_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Z2_MAX_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Z2_MAX_PIN), "Z2_MAX_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Z2_MAX_PIN), "Z2_MAX_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Z2_MAX_PIN);
     #endif
   #endif
@@ -212,7 +302,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Z2_MIN_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Z2_MIN_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Z2_MIN_PIN), "Z2_MIN_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Z2_MIN_PIN), "Z2_MIN_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Z2_MIN_PIN);
     #endif
   #endif
@@ -220,7 +310,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Z3_MAX_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Z3_MAX_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Z3_MAX_PIN), "Z3_MAX_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Z3_MAX_PIN), "Z3_MAX_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Z3_MAX_PIN);
     #endif
   #endif
@@ -228,7 +318,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Z3_MIN_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Z3_MIN_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Z3_MIN_PIN), "Z3_MIN_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Z3_MIN_PIN), "Z3_MIN_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Z3_MIN_PIN);
     #endif
   #endif
@@ -236,7 +326,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Z4_MAX_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Z4_MAX_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Z4_MAX_PIN), "Z4_MAX_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Z4_MAX_PIN), "Z4_MAX_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Z4_MAX_PIN);
     #endif
   #endif
@@ -244,7 +334,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Z4_MIN_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Z4_MIN_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Z4_MIN_PIN), "Z4_MIN_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Z4_MIN_PIN), "Z4_MIN_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Z4_MIN_PIN);
     #endif
   #endif
@@ -252,7 +342,7 @@ void setup_endstop_interrupts() {
     #if (digitalPinToInterrupt(Z_MIN_PROBE_PIN) != NOT_AN_INTERRUPT)
       _ATTACH(Z_MIN_PROBE_PIN);
     #else
-      static_assert(digitalPinHasPCICR(Z_MIN_PROBE_PIN), "Z_MIN_PROBE_PIN is not interrupt-capable");
+      static_assert(digitalPinHasPCICR(Z_MIN_PROBE_PIN), "Z_MIN_PROBE_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue.");
       pciSetup(Z_MIN_PROBE_PIN);
     #endif
   #endif
diff --git a/Marlin/src/HAL/AVR/fast_pwm.cpp b/Marlin/src/HAL/AVR/fast_pwm.cpp
index 238c1124ad..d361aaab38 100644
--- a/Marlin/src/HAL/AVR/fast_pwm.cpp
+++ b/Marlin/src/HAL/AVR/fast_pwm.cpp
@@ -21,11 +21,7 @@
  */
 #ifdef __AVR__
 
-#include "../../inc/MarlinConfigPre.h"
-
-#if NEEDS_HARDWARE_PWM // Specific meta-flag for features that mandate PWM
-
-#include "HAL.h"
+#include "../../inc/MarlinConfig.h"
 
 struct Timer {
   volatile uint8_t* TCCRnQ[3];  // max 3 TCCR registers per timer
@@ -33,250 +29,194 @@ struct Timer {
   volatile uint16_t* ICRn;      // max 1 ICR register per timer
   uint8_t n;                    // the timer number [0->5]
   uint8_t q;                    // the timer output [0->2] (A->C)
+  bool isPWM;                   // True if pin is a "hardware timer"
+  bool isProtected;             // True if timer is protected
 };
 
+// Macros for the Timer structure
+#define _SET_WGMnQ(T, V) do{ \
+    *(T.TCCRnQ)[0] = (*(T.TCCRnQ)[0] & ~(0x3 << 0)) | (( int(V)       & 0x3) << 0); \
+    *(T.TCCRnQ)[1] = (*(T.TCCRnQ)[1] & ~(0x3 << 3)) | (((int(V) >> 2) & 0x3) << 3); \
+  }while(0)
+
+// Set TCCR CS bits
+#define _SET_CSn(T, V) (*(T.TCCRnQ)[1] = (*(T.TCCRnQ[1]) & ~(0x7 << 0)) | ((int(V) & 0x7) << 0))
+
+// Set TCCR COM bits
+#define _SET_COMnQ(T, Q, V) (*(T.TCCRnQ)[0] = (*(T.TCCRnQ)[0] & ~(0x3 << (6-2*(Q)))) | (int(V) << (6-2*(Q))))
+
+// Set OCRnQ register
+#define _SET_OCRnQ(T, Q, V) (*(T.OCRnQ)[Q] = int(V) & 0xFFFF)
+
+// Set ICRn register (one per timer)
+#define _SET_ICRn(T, V) (*(T.ICRn) = int(V) & 0xFFFF)
+
 /**
- * get_pwm_timer
- *  Get the timer information and register of the provided pin.
- *  Return a Timer struct containing this information.
- *  Used by set_pwm_frequency, set_pwm_duty
+ * Return a Timer struct describing a pin's timer.
  */
-Timer get_pwm_timer(const pin_t pin) {
+const Timer get_pwm_timer(const pin_t pin) {
+
   uint8_t q = 0;
+
   switch (digitalPinToTimer(pin)) {
-    // Protect reserved timers (TIMER0 & TIMER1)
     #ifdef TCCR0A
-      #if !AVR_AT90USB1286_FAMILY
-        case TIMER0A:
-      #endif
-      case TIMER0B:
+      IF_DISABLED(AVR_AT90USB1286_FAMILY, case TIMER0A:)
     #endif
     #ifdef TCCR1A
       case TIMER1A: case TIMER1B:
     #endif
-                                        break;
-    #if defined(TCCR2) || defined(TCCR2A)
-      #ifdef TCCR2
-        case TIMER2: {
-          Timer timer = {
-            /*TCCRnQ*/  { &TCCR2, nullptr, nullptr },
-            /*OCRnQ*/   { (uint16_t*)&OCR2, nullptr, nullptr },
-            /*ICRn*/      nullptr,
-            /*n, q*/      2, 0
-          };
-        }
-      #elif defined(TCCR2A)
-        #if ENABLED(USE_OCR2A_AS_TOP)
-          case TIMER2A:   break; // protect TIMER2A
-          case TIMER2B: {
-            Timer timer = {
-              /*TCCRnQ*/  { &TCCR2A,  &TCCR2B,  nullptr },
-              /*OCRnQ*/   { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr },
-              /*ICRn*/      nullptr,
-              /*n, q*/      2, 1
-            };
-            return timer;
-          }
-        #else
-          case TIMER2B:   ++q;
-          case TIMER2A: {
-            Timer timer = {
-              /*TCCRnQ*/  { &TCCR2A,  &TCCR2B,  nullptr },
-              /*OCRnQ*/   { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr },
-              /*ICRn*/      nullptr,
-                            2, q
-            };
-            return timer;
-          }
-        #endif
-      #endif
+
+    break;    // Protect reserved timers (TIMER0 & TIMER1)
+
+    #ifdef TCCR0A
+      case TIMER0B:   // Protected timer, but allow setting the duty cycle on OCR0B for pin D4 only
+        return Timer({ { &TCCR0A, nullptr, nullptr }, { (uint16_t*)&OCR0A, (uint16_t*)&OCR0B, nullptr }, nullptr, 0, 1, true, true });
     #endif
+
+    #if HAS_TCCR2
+      case TIMER2:
+        return Timer({ { &TCCR2, nullptr, nullptr }, { (uint16_t*)&OCR2, nullptr, nullptr }, nullptr, 2, 0, true, false });
+    #elif ENABLED(USE_OCR2A_AS_TOP)
+      case TIMER2A: break; // Protect TIMER2A since its OCR is used by TIMER2B
+      case TIMER2B:
+        return Timer({ { &TCCR2A, &TCCR2B, nullptr }, { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr }, nullptr, 2, 1, true, false });
+    #elif defined(TCCR2A)
+      case TIMER2B: ++q; case TIMER2A:
+        return Timer({ { &TCCR2A, &TCCR2B, nullptr }, { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr }, nullptr, 2, q, true, false });
+    #endif
+
     #ifdef OCR3C
-      case TIMER3C:   ++q;
-      case TIMER3B:   ++q;
-      case TIMER3A: {
-        Timer timer = {
-          /*TCCRnQ*/  { &TCCR3A,  &TCCR3B,  &TCCR3C },
-          /*OCRnQ*/   { &OCR3A,   &OCR3B,   &OCR3C },
-          /*ICRn*/      &ICR3,
-          /*n, q*/      3, q
-        };
-        return timer;
-      }
+      case TIMER3C: ++q; case TIMER3B: ++q; case TIMER3A:
+        return Timer({ { &TCCR3A, &TCCR3B, &TCCR3C }, { &OCR3A, &OCR3B, &OCR3C  }, &ICR3, 3, q, true, false });
     #elif defined(OCR3B)
-      case TIMER3B:   ++q;
-      case TIMER3A: {
-        Timer timer = {
-          /*TCCRnQ*/  { &TCCR3A,  &TCCR3B,  nullptr },
-          /*OCRnQ*/   { &OCR3A,   &OCR3B,  nullptr },
-          /*ICRn*/      &ICR3,
-          /*n, q*/      3, q
-        };
-        return timer;
-      }
+      case TIMER3B: ++q; case TIMER3A:
+        return Timer({ { &TCCR3A, &TCCR3B, nullptr }, { &OCR3A, &OCR3B, nullptr }, &ICR3, 3, q, true, false });
     #endif
+
     #ifdef TCCR4A
-      case TIMER4C:   ++q;
-      case TIMER4B:   ++q;
-      case TIMER4A: {
-        Timer timer = {
-          /*TCCRnQ*/  { &TCCR4A,  &TCCR4B,  &TCCR4C },
-          /*OCRnQ*/   { &OCR4A,   &OCR4B,   &OCR4C },
-          /*ICRn*/      &ICR4,
-          /*n, q*/      4, q
-        };
-        return timer;
-      }
+      case TIMER4C: ++q; case TIMER4B: ++q; case TIMER4A:
+        return Timer({ { &TCCR4A, &TCCR4B, &TCCR4C }, { &OCR4A, &OCR4B, &OCR4C }, &ICR4, 4, q, true, false });
     #endif
+
     #ifdef TCCR5A
-      case TIMER5C:   ++q;
-      case TIMER5B:   ++q;
-      case TIMER5A: {
-        Timer timer = {
-          /*TCCRnQ*/  { &TCCR5A,  &TCCR5B,  &TCCR5C },
-          /*OCRnQ*/   { &OCR5A,   &OCR5B,   &OCR5C },
-          /*ICRn*/      &ICR5,
-          /*n, q*/      5, q
-        };
-        return timer;
-      }
+      case TIMER5C: ++q; case TIMER5B: ++q; case TIMER5A:
+        return Timer({ { &TCCR5A, &TCCR5B, &TCCR5C }, { &OCR5A, &OCR5B, &OCR5C }, &ICR5, 5, q, true, false });
     #endif
   }
-  Timer timer = {
-      /*TCCRnQ*/  { nullptr, nullptr, nullptr },
-      /*OCRnQ*/   { nullptr, nullptr, nullptr },
-      /*ICRn*/      nullptr,
-                    0, 0
-  };
-  return timer;
+
+  return Timer();
 }
 
-void set_pwm_frequency(const pin_t pin, int f_desired) {
-  Timer timer = get_pwm_timer(pin);
-  if (timer.n == 0) return; // Don't proceed if protected timer or not recognised
-  uint16_t size;
-  if (timer.n == 2) size = 255; else size = 65535;
+void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) {
+  const Timer timer = get_pwm_timer(pin);
+  if (timer.isProtected || !timer.isPWM) return; // Don't proceed if protected timer or not recognized
 
-  uint16_t res = 255;   // resolution (TOP value)
-  uint8_t j = 0;        // prescaler index
-  uint8_t wgm = 1;      // waveform generation mode
+  const bool is_timer2 = timer.n == 2;
+  const uint16_t maxtop = is_timer2 ? 0xFF : 0xFFFF;
+
+  uint16_t res = 0xFF;        // resolution (TOP value)
+  uint8_t j = CS_NONE;        // prescaler index
+  uint8_t wgm = WGM_PWM_PC_8; // waveform generation mode
 
   // Calculating the prescaler and resolution to use to achieve closest frequency
   if (f_desired != 0) {
-    int f = (F_CPU) / (2 * 1024 * size) + 1; // Initialize frequency as lowest (non-zero) achievable
-    uint16_t prescaler[] = { 0, 1, 8, /*TIMER2 ONLY*/32, 64, /*TIMER2 ONLY*/128, 256, 1024 };
+    constexpr uint16_t prescaler[] = { 1, 8, (32), 64, (128), 256, 1024 };  // (*) are Timer 2 only
+    uint16_t f = (F_CPU) / (2 * 1024 * maxtop) + 1;           // Start with the lowest non-zero frequency achievable (1 or 31)
 
-    // loop over prescaler values
-    LOOP_S_L_N(i, 1, 8) {
-      uint16_t res_temp_fast = 255, res_temp_phase_correct = 255;
-      if (timer.n == 2) {
-        // No resolution calculation for TIMER2 unless enabled USE_OCR2A_AS_TOP
-        #if ENABLED(USE_OCR2A_AS_TOP)
-          const uint16_t rtf = (F_CPU) / (prescaler[i] * f_desired);
-          res_temp_fast = rtf - 1;
-          res_temp_phase_correct = rtf / 2;
+    LOOP_L_N(i, COUNT(prescaler)) {                           // Loop through all prescaler values
+      const uint16_t p = prescaler[i];
+      uint16_t res_fast_temp, res_pc_temp;
+      if (is_timer2) {
+        #if ENABLED(USE_OCR2A_AS_TOP)                         // No resolution calculation for TIMER2 unless enabled USE_OCR2A_AS_TOP
+          const uint16_t rft = (F_CPU) / (p * f_desired);
+          res_fast_temp = rft - 1;
+          res_pc_temp = rft / 2;
+        #else
+          res_fast_temp = res_pc_temp = maxtop;
         #endif
       }
       else {
-        // Skip TIMER2 specific prescalers when not TIMER2
-        if (i == 3 || i == 5) continue;
-        const uint16_t rtf = (F_CPU) / (prescaler[i] * f_desired);
-        res_temp_fast = rtf - 1;
-        res_temp_phase_correct = rtf / 2;
+        if (p == 32 || p == 128) continue;                    // Skip TIMER2 specific prescalers when not TIMER2
+        const uint16_t rft = (F_CPU) / (p * f_desired);
+        res_fast_temp = rft - 1;
+        res_pc_temp = rft / 2;
       }
 
-      LIMIT(res_temp_fast, 1U, size);
-      LIMIT(res_temp_phase_correct, 1U, size);
+      LIMIT(res_fast_temp, 1U, maxtop);
+      LIMIT(res_pc_temp, 1U, maxtop);
+
       // Calculate frequencies of test prescaler and resolution values
-      const int f_temp_fast = (F_CPU) / (prescaler[i] * (1 + res_temp_fast)),
-                f_temp_phase_correct = (F_CPU) / (2 * prescaler[i] * res_temp_phase_correct),
-                f_diff = ABS(f - f_desired),
-                f_fast_diff = ABS(f_temp_fast - f_desired),
-                f_phase_diff = ABS(f_temp_phase_correct - f_desired);
+      const uint16_t f_fast_temp = (F_CPU) / (p * (1 + res_fast_temp)),
+                     f_pc_temp   = (F_CPU) / (2 * p * res_pc_temp);
+      const int      f_diff      = _MAX(f, f_desired) - _MIN(f, f_desired),
+                     f_fast_diff = _MAX(f_fast_temp, f_desired) - _MIN(f_fast_temp, f_desired),
+                     f_pc_diff   = _MAX(f_pc_temp, f_desired) - _MIN(f_pc_temp, f_desired);
 
-      // If FAST values are closest to desired f
-      if (f_fast_diff < f_diff && f_fast_diff <= f_phase_diff) {
-        // Remember this combination
-        f = f_temp_fast;
-        res = res_temp_fast;
-        j = i;
+      if (f_fast_diff < f_diff && f_fast_diff <= f_pc_diff) { // FAST values are closest to desired f
         // Set the Wave Generation Mode to FAST PWM
-        if (timer.n == 2) {
-          wgm = (
-            #if ENABLED(USE_OCR2A_AS_TOP)
-              WGM2_FAST_PWM_OCR2A
-            #else
-              WGM2_FAST_PWM
-            #endif
-          );
-        }
-        else wgm = WGM_FAST_PWM_ICRn;
+        wgm = is_timer2 ? uint8_t(TERN(USE_OCR2A_AS_TOP, WGM2_FAST_PWM_OCR2A, WGM2_FAST_PWM)) : uint8_t(WGM_FAST_PWM_ICRn);
+        // Remember this combination
+        f = f_fast_temp; res = res_fast_temp; j = i + 1;
       }
-      // If PHASE CORRECT values are closes to desired f
-      else if (f_phase_diff < f_diff) {
-        f = f_temp_phase_correct;
-        res = res_temp_phase_correct;
-        j = i;
+      else if (f_pc_diff < f_diff) {                          // PHASE CORRECT values are closes to desired f
         // Set the Wave Generation Mode to PWM PHASE CORRECT
-        if (timer.n == 2) {
-          wgm = (
-            #if ENABLED(USE_OCR2A_AS_TOP)
-              WGM2_PWM_PC_OCR2A
-            #else
-              WGM2_PWM_PC
-            #endif
-          );
-        }
-        else wgm = WGM_PWM_PC_ICRn;
+        wgm = is_timer2 ? uint8_t(TERN(USE_OCR2A_AS_TOP, WGM2_PWM_PC_OCR2A, WGM2_PWM_PC)) : uint8_t(WGM_PWM_PC_ICRn);
+        f = f_pc_temp; res = res_pc_temp; j = i + 1;
       }
     }
   }
-  _SET_WGMnQ(timer.TCCRnQ, wgm);
-  _SET_CSn(timer.TCCRnQ, j);
 
-  if (timer.n == 2) {
-    #if ENABLED(USE_OCR2A_AS_TOP)
-      _SET_OCRnQ(timer.OCRnQ, 0, res);  // Set OCR2A value (TOP) = res
-    #endif
+  _SET_WGMnQ(timer, wgm);
+  _SET_CSn(timer, j);
+
+  if (is_timer2) {
+    TERN_(USE_OCR2A_AS_TOP, _SET_OCRnQ(timer, 0, res)); // Set OCR2A value (TOP) = res
   }
   else
-    _SET_ICRn(timer.ICRn, res);         // Set ICRn value (TOP) = res
+    _SET_ICRn(timer, res);                              // Set ICRn value (TOP) = res
 }
 
-void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) {
+void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) {
   // If v is 0 or v_size (max), digitalWrite to LOW or HIGH.
-  // Note that digitalWrite also disables pwm output for us (sets COM bit to 0)
+  // Note that digitalWrite also disables PWM output for us (sets COM bit to 0)
   if (v == 0)
     digitalWrite(pin, invert);
   else if (v == v_size)
     digitalWrite(pin, !invert);
   else {
-    Timer timer = get_pwm_timer(pin);
-    if (timer.n == 0) return; // Don't proceed if protected timer or not recognised
-    // Set compare output mode to CLEAR -> SET or SET -> CLEAR (if inverted)
-    _SET_COMnQ(timer.TCCRnQ, (timer.q
-        #ifdef TCCR2
-          + (timer.q == 2) // COM20 is on bit 4 of TCCR2, thus requires q + 1 in the macro
-        #endif
-      ), COM_CLEAR_SET + invert
-    );
-
-    uint16_t top;
-    if (timer.n == 2) { // if TIMER2
-      top = (
-        #if ENABLED(USE_OCR2A_AS_TOP)
-          *timer.OCRnQ[0] // top = OCR2A
-        #else
-          255 // top = 0xFF (max)
-        #endif
-      );
+    const Timer timer = get_pwm_timer(pin);
+    if (timer.isPWM) {
+      if (timer.n == 0) {
+        _SET_COMnQ(timer, timer.q, COM_CLEAR_SET);  // Only allow a TIMER0B select...
+        _SET_OCRnQ(timer, timer.q, v);              // ...and OCR0B duty update. For output pin D4 no frequency changes are permitted.
+      }
+      else if (!timer.isProtected) {
+        const uint16_t top = timer.n == 2 ? TERN(USE_OCR2A_AS_TOP, *timer.OCRnQ[0], 255) : *timer.ICRn;
+        _SET_COMnQ(timer, SUM_TERN(HAS_TCCR2, timer.q, timer.q == 2), COM_CLEAR_SET + invert);   // COM20 is on bit 4 of TCCR2, so +1 for q==2
+        _SET_OCRnQ(timer, timer.q, uint16_t(uint32_t(v) * top / v_size)); // Scale 8/16-bit v to top value
+      }
     }
     else
-      top = *timer.ICRn; // top = ICRn
-
-    _SET_OCRnQ(timer.OCRnQ, timer.q, v * float(top) / float(v_size)); // Scale 8/16-bit v to top value
+      digitalWrite(pin, v < v_size / 2 ? LOW : HIGH);
   }
 }
 
-#endif // NEEDS_HARDWARE_PWM
+void MarlinHAL::init_pwm_timers() {
+  // Init some timer frequencies to a default 1KHz
+  const pin_t pwm_pin[] = {
+    #ifdef __AVR_ATmega2560__
+      10, 5, 6, 46
+    #elif defined(__AVR_ATmega1280__)
+      12, 31
+    #elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284__)
+      15, 6
+    #elif defined(__AVR_AT90USB1286__) || defined(__AVR_mega64) || defined(__AVR_mega128)
+      16, 24
+    #endif
+  };
+
+  LOOP_L_N(i, COUNT(pwm_pin))
+    set_pwm_frequency(pwm_pin[i], 1000);
+}
+
 #endif // __AVR__
diff --git a/Marlin/src/HAL/AVR/fastio.cpp b/Marlin/src/HAL/AVR/fastio.cpp
index b51d7f9768..5c6ef18915 100644
--- a/Marlin/src/HAL/AVR/fastio.cpp
+++ b/Marlin/src/HAL/AVR/fastio.cpp
@@ -241,11 +241,11 @@ uint8_t extDigitalRead(const int8_t pin) {
  *
  * DC values -1.0 to 1.0. Negative duty cycle inverts the pulse.
  */
-uint16_t set_pwm_frequency_hz(const float &hz, const float dca, const float dcb, const float dcc) {
+uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb, const float dcc) {
   float count = 0;
   if (hz > 0 && (dca || dcb || dcc)) {
     count = float(F_CPU) / hz;            // 1x prescaler, TOP for 16MHz base freq.
-    uint16_t prescaler;                   // Range of 30.5Hz (65535) 64.5KHz (>31)
+    uint16_t prescaler;                   // Range of 30.5Hz (65535) 64.5kHz (>31)
 
          if (count >= 255. * 256.) { prescaler = 1024; SET_CS(5, PRESCALER_1024); }
     else if (count >= 255. * 64.)  { prescaler = 256;  SET_CS(5,  PRESCALER_256); }
@@ -257,7 +257,7 @@ uint16_t set_pwm_frequency_hz(const float &hz, const float dca, const float dcb,
     const float pwm_top = round(count);   // Get the rounded count
 
     ICR5 = (uint16_t)pwm_top - 1;         // Subtract 1 for TOP
-    OCR5A = pwm_top * ABS(dca);          // Update and scale DCs
+    OCR5A = pwm_top * ABS(dca);           // Update and scale DCs
     OCR5B = pwm_top * ABS(dcb);
     OCR5C = pwm_top * ABS(dcc);
     _SET_COM(5, A, dca ? (dca < 0 ? COM_SET_CLEAR : COM_CLEAR_SET) : COM_NORMAL); // Set compare modes
@@ -267,17 +267,17 @@ uint16_t set_pwm_frequency_hz(const float &hz, const float dca, const float dcb,
     SET_WGM(5, FAST_PWM_ICRn);            // Fast PWM with ICR5 as TOP
 
     //SERIAL_ECHOLNPGM("Timer 5 Settings:");
-    //SERIAL_ECHOLNPAIR("  Prescaler=", prescaler);
-    //SERIAL_ECHOLNPAIR("        TOP=", ICR5);
-    //SERIAL_ECHOLNPAIR("      OCR5A=", OCR5A);
-    //SERIAL_ECHOLNPAIR("      OCR5B=", OCR5B);
-    //SERIAL_ECHOLNPAIR("      OCR5C=", OCR5C);
+    //SERIAL_ECHOLNPGM("  Prescaler=", prescaler);
+    //SERIAL_ECHOLNPGM("        TOP=", ICR5);
+    //SERIAL_ECHOLNPGM("      OCR5A=", OCR5A);
+    //SERIAL_ECHOLNPGM("      OCR5B=", OCR5B);
+    //SERIAL_ECHOLNPGM("      OCR5C=", OCR5C);
   }
   else {
     // Restore the default for Timer 5
     SET_WGM(5, PWM_PC_8);                 // PWM 8-bit (Phase Correct)
     SET_COMS(5, NORMAL, NORMAL, NORMAL);  // Do nothing
-    SET_CS(5, PRESCALER_64);              // 16MHz / 64 = 250KHz
+    SET_CS(5, PRESCALER_64);              // 16MHz / 64 = 250kHz
     OCR5A = OCR5B = OCR5C = 0;
   }
   return round(count);
diff --git a/Marlin/src/HAL/AVR/fastio.h b/Marlin/src/HAL/AVR/fastio.h
index dd01634661..612ab902e3 100644
--- a/Marlin/src/HAL/AVR/fastio.h
+++ b/Marlin/src/HAL/AVR/fastio.h
@@ -118,7 +118,7 @@
  */
 
 // Waveform Generation Modes
-enum WaveGenMode : char {
+enum WaveGenMode : uint8_t {
   WGM_NORMAL,          //  0
   WGM_PWM_PC_8,        //  1
   WGM_PWM_PC_9,        //  2
@@ -138,19 +138,19 @@ enum WaveGenMode : char {
 };
 
 // Wavefore Generation Modes (Timer 2 only)
-enum WaveGenMode2 : char {
-  WGM2_NORMAL,          // 0
-  WGM2_PWM_PC,          // 1
-  WGM2_CTC_OCR2A,       // 2
-  WGM2_FAST_PWM,        // 3
-  WGM2_reserved_1,      // 4
-  WGM2_PWM_PC_OCR2A,    // 5
-  WGM2_reserved_2,      // 6
-  WGM2_FAST_PWM_OCR2A,  // 7
+enum WaveGenMode2 : uint8_t {
+  WGM2_NORMAL,         // 0
+  WGM2_PWM_PC,         // 1
+  WGM2_CTC_OCR2A,      // 2
+  WGM2_FAST_PWM,       // 3
+  WGM2_reserved_1,     // 4
+  WGM2_PWM_PC_OCR2A,   // 5
+  WGM2_reserved_2,     // 6
+  WGM2_FAST_PWM_OCR2A, // 7
 };
 
 // Compare Modes
-enum CompareMode : char {
+enum CompareMode : uint8_t {
   COM_NORMAL,          //  0
   COM_TOGGLE,          //  1  Non-PWM: OCnx ... Both PWM (WGM 9,11,14,15): OCnA only ... else NORMAL
   COM_CLEAR_SET,       //  2  Non-PWM: OCnx ... Fast PWM: OCnx/Bottom ... PF-FC: OCnx Up/Down
@@ -158,7 +158,7 @@ enum CompareMode : char {
 };
 
 // Clock Sources
-enum ClockSource : char {
+enum ClockSource : uint8_t {
   CS_NONE,             //  0
   CS_PRESCALER_1,      //  1
   CS_PRESCALER_8,      //  2
@@ -170,7 +170,7 @@ enum ClockSource : char {
 };
 
 // Clock Sources (Timer 2 only)
-enum ClockSource2 : char {
+enum ClockSource2 : uint8_t {
   CS2_NONE,            //  0
   CS2_PRESCALER_1,     //  1
   CS2_PRESCALER_8,     //  2
@@ -203,40 +203,33 @@ enum ClockSource2 : char {
     TCCR##T##B = (TCCR##T##B & ~(0x3 << WGM##T##2)) | (((int(V) >> 2) & 0x3) << WGM##T##2); \
   }while(0)
 #define SET_WGM(T,V) _SET_WGM(T,WGM_##V)
-// Runtime (see set_pwm_frequency):
-#define _SET_WGMnQ(TCCRnQ, V) do{ \
-    *(TCCRnQ)[0] = (*(TCCRnQ)[0] & ~(0x3 << 0)) | (( int(V)       & 0x3) << 0); \
-    *(TCCRnQ)[1] = (*(TCCRnQ)[1] & ~(0x3 << 3)) | (((int(V) >> 2) & 0x3) << 3); \
-  }while(0)
 
 // Set Clock Select bits
 // Ex: SET_CS3(PRESCALER_64);
+#ifdef TCCR2
+  #define HAS_TCCR2 1
+#endif
 #define _SET_CS(T,V) (TCCR##T##B = (TCCR##T##B & ~(0x7 << CS##T##0)) | ((int(V) & 0x7) << CS##T##0))
 #define _SET_CS0(V) _SET_CS(0,V)
 #define _SET_CS1(V) _SET_CS(1,V)
-#ifdef TCCR2
-  #define _SET_CS2(V) (TCCR2 = (TCCR2 & ~(0x7 << CS20)) | (int(V) << CS20))
-#else
-  #define _SET_CS2(V) _SET_CS(2,V)
-#endif
 #define _SET_CS3(V) _SET_CS(3,V)
 #define _SET_CS4(V) _SET_CS(4,V)
 #define _SET_CS5(V) _SET_CS(5,V)
 #define SET_CS0(V) _SET_CS0(CS_##V)
 #define SET_CS1(V) _SET_CS1(CS_##V)
-#ifdef TCCR2
+
+#if HAS_TCCR2
+  #define _SET_CS2(V) (TCCR2 = (TCCR2 & ~(0x7 << CS20)) | (int(V) << CS20))
   #define SET_CS2(V) _SET_CS2(CS2_##V)
 #else
+  #define _SET_CS2(V) _SET_CS(2,V)
   #define SET_CS2(V) _SET_CS2(CS_##V)
 #endif
+
 #define SET_CS3(V) _SET_CS3(CS_##V)
 #define SET_CS4(V) _SET_CS4(CS_##V)
 #define SET_CS5(V) _SET_CS5(CS_##V)
 #define SET_CS(T,V) SET_CS##T(V)
-// Runtime (see set_pwm_frequency)
-#define _SET_CSn(TCCRnQ, V) do{ \
-    (*(TCCRnQ)[1] = (*(TCCRnQ[1]) & ~(0x7 << 0)) | ((int(V) & 0x7) << 0)); \
-  }while(0)
 
 // Set Compare Mode bits
 // Ex: SET_COMS(4,CLEAR_SET,CLEAR_SET,CLEAR_SET);
@@ -246,22 +239,6 @@ enum ClockSource2 : char {
 #define SET_COMB(T,V) SET_COM(T,B,V)
 #define SET_COMC(T,V) SET_COM(T,C,V)
 #define SET_COMS(T,V1,V2,V3) do{ SET_COMA(T,V1); SET_COMB(T,V2); SET_COMC(T,V3); }while(0)
-// Runtime (see set_pwm_duty)
-#define _SET_COMnQ(TCCRnQ, Q, V) do{ \
-    (*(TCCRnQ)[0] = (*(TCCRnQ)[0] & ~(0x3 << (6-2*(Q)))) | (int(V) << (6-2*(Q)))); \
-  }while(0)
-
-// Set OCRnQ register
-// Runtime (see set_pwm_duty):
-#define _SET_OCRnQ(OCRnQ, Q, V) do{ \
-    (*(OCRnQ)[(Q)] = (0x0000) | (int(V) & 0xFFFF)); \
-  }while(0)
-
-// Set ICRn register (one per timer)
-// Runtime (see set_pwm_frequency)
-#define _SET_ICRn(ICRn, V) do{ \
-    (*(ICRn) = (0x0000) | (int(V) & 0xFFFF)); \
-  }while(0)
 
 // Set Noise Canceler bit
 // Ex: SET_ICNC(2,1)
@@ -284,8 +261,8 @@ enum ClockSource2 : char {
  * PWM availability macros
  */
 
-// Determine which harware PWMs are already in use
-#define _PWM_CHK_FAN_B(P) (P == E0_AUTO_FAN_PIN || P == E1_AUTO_FAN_PIN || P == E2_AUTO_FAN_PIN || P == E3_AUTO_FAN_PIN || P == E4_AUTO_FAN_PIN || P == E5_AUTO_FAN_PIN || P == E6_AUTO_FAN_PIN || P == E7_AUTO_FAN_PIN || P == CHAMBER_AUTO_FAN_PIN)
+// Determine which hardware PWMs are already in use
+#define _PWM_CHK_FAN_B(P) (P == E0_AUTO_FAN_PIN || P == E1_AUTO_FAN_PIN || P == E2_AUTO_FAN_PIN || P == E3_AUTO_FAN_PIN || P == E4_AUTO_FAN_PIN || P == E5_AUTO_FAN_PIN || P == E6_AUTO_FAN_PIN || P == E7_AUTO_FAN_PIN || P == CHAMBER_AUTO_FAN_PIN || P == COOLER_AUTO_FAN_PIN)
 #if PIN_EXISTS(CONTROLLER_FAN)
   #define PWM_CHK_FAN_B(P) (_PWM_CHK_FAN_B(P) || P == CONTROLLER_FAN_PIN)
 #else
@@ -316,11 +293,11 @@ enum ClockSource2 : char {
 
 #if HAS_MOTOR_CURRENT_PWM
   #if PIN_EXISTS(MOTOR_CURRENT_PWM_XY)
-    #define PWM_CHK_MOTOR_CURRENT(P) (P == MOTOR_CURRENT_PWM_E || P == MOTOR_CURRENT_PWM_Z || P == MOTOR_CURRENT_PWM_XY)
+    #define PWM_CHK_MOTOR_CURRENT(P) (P == MOTOR_CURRENT_PWM_E || P == MOTOR_CURRENT_PWM_E0  || P == MOTOR_CURRENT_PWM_E1 || P == MOTOR_CURRENT_PWM_Z || P == MOTOR_CURRENT_PWM_XY)
   #elif PIN_EXISTS(MOTOR_CURRENT_PWM_Z)
-    #define PWM_CHK_MOTOR_CURRENT(P) (P == MOTOR_CURRENT_PWM_E || P == MOTOR_CURRENT_PWM_Z)
+    #define PWM_CHK_MOTOR_CURRENT(P) (P == MOTOR_CURRENT_PWM_E || P == MOTOR_CURRENT_PWM_E0  || P == MOTOR_CURRENT_PWM_E1 || P == MOTOR_CURRENT_PWM_Z)
   #else
-    #define PWM_CHK_MOTOR_CURRENT(P) (P == MOTOR_CURRENT_PWM_E)
+    #define PWM_CHK_MOTOR_CURRENT(P) (P == MOTOR_CURRENT_PWM_E || P == MOTOR_CURRENT_PWM_E0  || P == MOTOR_CURRENT_PWM_E1)
   #endif
 #else
   #define PWM_CHK_MOTOR_CURRENT(P) false
diff --git a/Marlin/src/HAL/AVR/inc/SanityCheck.h b/Marlin/src/HAL/AVR/inc/SanityCheck.h
index 731cf92865..ff1610f741 100644
--- a/Marlin/src/HAL/AVR/inc/SanityCheck.h
+++ b/Marlin/src/HAL/AVR/inc/SanityCheck.h
@@ -25,25 +25,67 @@
  * Test AVR-specific configuration values for errors at compile-time.
  */
 
+/**
+ * Check for common serial pin conflicts
+ */
+#define CHECK_SERIAL_PIN(N) ( \
+     X_STOP_PIN == N || Y_STOP_PIN == N || Z_STOP_PIN == N \
+  || X_MIN_PIN  == N || Y_MIN_PIN  == N || Z_MIN_PIN  == N \
+  || X_MAX_PIN  == N || Y_MAX_PIN  == N || Z_MAX_PIN  == N \
+  || X_STEP_PIN == N || Y_STEP_PIN == N || Z_STEP_PIN == N \
+  || X_DIR_PIN  == N || Y_DIR_PIN  == N || Z_DIR_PIN  == N \
+  || X_ENA_PIN  == N || Y_ENA_PIN  == N || Z_ENA_PIN  == N \
+  || BTN_EN1    == N || BTN_EN2    == N \
+)
+#if SERIAL_IN_USE(0)
+  // D0-D1. No known conflicts.
+#endif
+#if SERIAL_IN_USE(1)
+  #if NOT_TARGET(__AVR_ATmega644P__, __AVR_ATmega1284P__)
+    #if CHECK_SERIAL_PIN(18) || CHECK_SERIAL_PIN(19)
+      #error "Serial Port 1 pin D18 and/or D19 conflicts with another pin on the board."
+    #endif
+  #else
+    #if CHECK_SERIAL_PIN(10) || CHECK_SERIAL_PIN(11)
+      #error "Serial Port 1 pin D10 and/or D11 conflicts with another pin on the board."
+    #endif
+  #endif
+#endif
+#if SERIAL_IN_USE(2) && (CHECK_SERIAL_PIN(16) || CHECK_SERIAL_PIN(17))
+  #error "Serial Port 2 pin D16 and/or D17 conflicts with another pin on the board."
+#endif
+#if SERIAL_IN_USE(3) && (CHECK_SERIAL_PIN(14) || CHECK_SERIAL_PIN(15))
+  #error "Serial Port 3 pin D14 and/or D15 conflicts with another pin on the board."
+#endif
+#undef CHECK_SERIAL_PIN
+
 /**
  * Checks for FAST PWM
  */
-#if ENABLED(FAST_PWM_FAN) && (ENABLED(USE_OCR2A_AS_TOP) && defined(TCCR2))
-  #error "USE_OCR2A_AS_TOP does not apply to devices with a single output TIMER2"
+#if ALL(FAST_PWM_FAN, USE_OCR2A_AS_TOP, HAS_TCCR2)
+  #error "USE_OCR2A_AS_TOP does not apply to devices with a single output TIMER2."
+#endif
+
+/**
+ * Checks for SOFT PWM
+ */
+#if HAS_FAN0 && FAN_PIN == 9 && DISABLED(FAN_SOFT_PWM) && ENABLED(SPEAKER)
+  #error "FAN_PIN 9 Hardware PWM uses Timer 2 which conflicts with Arduino AVR Tone Timer (for SPEAKER)."
+  #error "Disable SPEAKER or enable FAN_SOFT_PWM."
 #endif
 
 /**
  * Sanity checks for Spindle / Laser PWM
  */
-#if ENABLED(SPINDLE_LASER_PWM)
+#if ENABLED(SPINDLE_LASER_USE_PWM)
   #include "../ServoTimers.h"   // Needed to check timer availability (_useTimer3)
   #if SPINDLE_LASER_PWM_PIN == 4 || WITHIN(SPINDLE_LASER_PWM_PIN, 11, 13)
     #error "Counter/Timer for SPINDLE_LASER_PWM_PIN is used by a system interrupt."
   #elif NUM_SERVOS > 0 && defined(_useTimer3) && (WITHIN(SPINDLE_LASER_PWM_PIN, 2, 3) || SPINDLE_LASER_PWM_PIN == 5)
     #error "Counter/Timer for SPINDLE_LASER_PWM_PIN is used by the servo system."
   #endif
-#elif defined(SPINDLE_LASER_FREQUENCY)
-  #error "SPINDLE_LASER_FREQUENCY requires SPINDLE_LASER_PWM."
+#elif SPINDLE_LASER_FREQUENCY
+  #error "SPINDLE_LASER_FREQUENCY requires SPINDLE_LASER_USE_PWM."
 #endif
 
 /**
@@ -56,3 +98,14 @@
 #if BOTH(HAS_TMC_SW_SERIAL, MONITOR_DRIVER_STATUS)
   #error "MONITOR_DRIVER_STATUS causes performance issues when used with SoftwareSerial-connected drivers. Disable MONITOR_DRIVER_STATUS or use hardware serial to continue."
 #endif
+
+/**
+ * Postmortem debugging
+ */
+#if ENABLED(POSTMORTEM_DEBUGGING)
+  #error "POSTMORTEM_DEBUGGING is not supported on AVR boards."
+#endif
+
+#if USING_PULLDOWNS
+  #error "PULLDOWN pin mode is not available on AVR boards."
+#endif
diff --git a/Marlin/src/HAL/AVR/math.h b/Marlin/src/HAL/AVR/math.h
index 7ede4accc0..7dd1018ff1 100644
--- a/Marlin/src/HAL/AVR/math.h
+++ b/Marlin/src/HAL/AVR/math.h
@@ -35,7 +35,7 @@
 // C B A is longIn1
 // D C B A is longIn2
 //
-static FORCE_INLINE uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2) {
+FORCE_INLINE static uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2) {
   uint8_t tmp1;
   uint8_t tmp2;
   uint16_t intRes;
@@ -89,7 +89,7 @@ static FORCE_INLINE uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2
 // uses:
 // r26 to store 0
 // r27 to store the byte 1 of the 24 bit result
-static FORCE_INLINE uint16_t MultiU16X8toH16(uint8_t charIn1, uint16_t intIn2) {
+FORCE_INLINE static uint16_t MultiU16X8toH16(uint8_t charIn1, uint16_t intIn2) {
   uint8_t tmp;
   uint16_t intRes;
   __asm__ __volatile__ (
diff --git a/Marlin/src/HAL/AVR/pinsDebug.h b/Marlin/src/HAL/AVR/pinsDebug.h
index dac6b1b150..dab4e44715 100644
--- a/Marlin/src/HAL/AVR/pinsDebug.h
+++ b/Marlin/src/HAL/AVR/pinsDebug.h
@@ -2,6 +2,9 @@
  * Marlin 3D Printer Firmware
  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
@@ -38,7 +41,7 @@
   // portModeRegister takes a different argument
   #define digitalPinToTimer_DEBUG(p) digitalPinToTimer(p)
   #define digitalPinToBitMask_DEBUG(p) digitalPinToBitMask(p)
-  #define digitalPinToPort_DEBUG(p) digitalPinToPort_Teensy(p)
+  #define digitalPinToPort_DEBUG(p) digitalPinToPort(p)
   #define GET_PINMODE(pin) (*portModeRegister(pin) & digitalPinToBitMask_DEBUG(pin))
 
 #elif AVR_ATmega2560_FAMILY_PLUS_70   // So we can access/display all the pins on boards using more than 70
@@ -71,7 +74,7 @@
 #define MULTI_NAME_PAD 26 // space needed to be pretty if not first name assigned to a pin
 
 void PRINT_ARRAY_NAME(uint8_t x) {
-  char *name_mem_pointer = (char*)pgm_read_ptr(&pin_array[x].name);
+  PGM_P const name_mem_pointer = (PGM_P)pgm_read_ptr(&pin_array[x].name);
   LOOP_L_N(y, MAX_NAME_LENGTH) {
     char temp_char = pgm_read_byte(name_mem_pointer + y);
     if (temp_char != 0)
@@ -99,7 +102,7 @@ void PRINT_ARRAY_NAME(uint8_t x) {
       return true;                                              \
     } else return false
 
-
+#define ABTEST(N) defined(TCCR##N##A) && defined(COM##N##A1)
 
 /**
  * Print a pin's PWM status.
@@ -110,7 +113,7 @@ static bool pwm_status(uint8_t pin) {
 
   switch (digitalPinToTimer_DEBUG(pin)) {
 
-    #if defined(TCCR0A) && defined(COM0A1)
+    #if ABTEST(0)
       #ifdef TIMER0A
         #if !AVR_AT90USB1286_FAMILY  // not available in Teensyduino type IDEs
           PWM_CASE(0, A);
@@ -119,20 +122,20 @@ static bool pwm_status(uint8_t pin) {
       PWM_CASE(0, B);
     #endif
 
-    #if defined(TCCR1A) && defined(COM1A1)
+    #if ABTEST(1)
       PWM_CASE(1, A);
       PWM_CASE(1, B);
-     #if defined(COM1C1) && defined(TIMER1C)
-      PWM_CASE(1, C);
-     #endif
+      #if defined(COM1C1) && defined(TIMER1C)
+        PWM_CASE(1, C);
+      #endif
     #endif
 
-    #if defined(TCCR2A) && defined(COM2A1)
+    #if ABTEST(2)
       PWM_CASE(2, A);
       PWM_CASE(2, B);
     #endif
 
-    #if defined(TCCR3A) && defined(COM3A1)
+    #if ABTEST(3)
       PWM_CASE(3, A);
       PWM_CASE(3, B);
       #ifdef COM3C1
@@ -146,7 +149,7 @@ static bool pwm_status(uint8_t pin) {
       PWM_CASE(4, C);
     #endif
 
-    #if defined(TCCR5A) && defined(COM5A1)
+    #if ABTEST(5)
       PWM_CASE(5, A);
       PWM_CASE(5, B);
       PWM_CASE(5, C);
@@ -163,16 +166,16 @@ static bool pwm_status(uint8_t pin) {
 const volatile uint8_t* const PWM_other[][3] PROGMEM = {
     { &TCCR0A, &TCCR0B, &TIMSK0 },
     { &TCCR1A, &TCCR1B, &TIMSK1 },
-  #if defined(TCCR2A) && defined(COM2A1)
+  #if ABTEST(2)
     { &TCCR2A, &TCCR2B, &TIMSK2 },
   #endif
-  #if defined(TCCR3A) && defined(COM3A1)
+  #if ABTEST(3)
     { &TCCR3A, &TCCR3B, &TIMSK3 },
   #endif
   #ifdef TCCR4A
     { &TCCR4A, &TCCR4B, &TIMSK4 },
   #endif
-  #if defined(TCCR5A) && defined(COM5A1)
+  #if ABTEST(5)
     { &TCCR5A, &TCCR5B, &TIMSK5 },
   #endif
 };
@@ -192,11 +195,11 @@ const volatile uint8_t* const PWM_OCR[][3] PROGMEM = {
    { (const uint8_t*)&OCR1A, (const uint8_t*)&OCR1B, 0 },
   #endif
 
-  #if defined(TCCR2A) && defined(COM2A1)
+  #if ABTEST(2)
     { &OCR2A, &OCR2B, 0 },
   #endif
 
-  #if defined(TCCR3A) && defined(COM3A1)
+  #if ABTEST(3)
     #ifdef COM3C1
       { (const uint8_t*)&OCR3A, (const uint8_t*)&OCR3B, (const uint8_t*)&OCR3C },
     #else
@@ -208,7 +211,7 @@ const volatile uint8_t* const PWM_OCR[][3] PROGMEM = {
     { (const uint8_t*)&OCR4A, (const uint8_t*)&OCR4B, (const uint8_t*)&OCR4C },
   #endif
 
-  #if defined(TCCR5A) && defined(COM5A1)
+  #if ABTEST(5)
     { (const uint8_t*)&OCR5A, (const uint8_t*)&OCR5B, (const uint8_t*)&OCR5C },
   #endif
 };
@@ -235,9 +238,9 @@ static void print_is_also_tied() { SERIAL_ECHOPGM(" is also tied to this pin");
 
 inline void com_print(const uint8_t N, const uint8_t Z) {
   const uint8_t *TCCRA = (uint8_t*)TCCR_A(N);
-  SERIAL_ECHOPGM("    COM");
-  SERIAL_CHAR('0' + N, Z);
-  SERIAL_ECHOPAIR(": ", int((*TCCRA >> (6 - Z * 2)) & 0x03));
+  SERIAL_ECHOPGM("    COM", AS_DIGIT(N));
+  SERIAL_CHAR(Z);
+  SERIAL_ECHOPGM(": ", int((*TCCRA >> (6 - Z * 2)) & 0x03));
 }
 
 void timer_prefix(uint8_t T, char L, uint8_t N) {  // T - timer    L - pwm  N - WGM bit layout
@@ -247,8 +250,8 @@ void timer_prefix(uint8_t T, char L, uint8_t N) {  // T - timer    L - pwm  N -
   uint8_t WGM = (((*TCCRB & _BV(WGM_2)) >> 1) | (*TCCRA & (_BV(WGM_0) | _BV(WGM_1))));
   if (N == 4) WGM |= ((*TCCRB & _BV(WGM_3)) >> 1);
 
-  SERIAL_ECHOPGM("    TIMER");
-  SERIAL_CHAR(T + '0', L);
+  SERIAL_ECHOPGM("    TIMER", AS_DIGIT(T));
+  SERIAL_CHAR(L);
   SERIAL_ECHO_SP(3);
 
   if (N == 3) {
@@ -259,22 +262,14 @@ void timer_prefix(uint8_t T, char L, uint8_t N) {  // T - timer    L - pwm  N -
     const uint16_t *OCRVAL16 = (uint16_t*)OCR_VAL(T, L - 'A');
     PWM_PRINT(*OCRVAL16);
   }
-  SERIAL_ECHOPAIR("    WGM: ", WGM);
+  SERIAL_ECHOPGM("    WGM: ", WGM);
   com_print(T,L);
-  SERIAL_ECHOPAIR("    CS: ", (*TCCRB & (_BV(CS_0) | _BV(CS_1) | _BV(CS_2)) ));
-
-  SERIAL_ECHOPGM("    TCCR");
-  SERIAL_CHAR(T + '0');
-  SERIAL_ECHOPAIR("A: ", *TCCRA);
-
-  SERIAL_ECHOPGM("    TCCR");
-  SERIAL_CHAR(T + '0');
-  SERIAL_ECHOPAIR("B: ", *TCCRB);
+  SERIAL_ECHOPGM("    CS: ", (*TCCRB & (_BV(CS_0) | _BV(CS_1) | _BV(CS_2)) ));
+  SERIAL_ECHOPGM("    TCCR", AS_DIGIT(T), "A: ", *TCCRA);
+  SERIAL_ECHOPGM("    TCCR", AS_DIGIT(T), "B: ", *TCCRB);
 
   const uint8_t *TMSK = (uint8_t*)TIMSK(T);
-  SERIAL_ECHOPGM("    TIMSK");
-  SERIAL_CHAR(T + '0');
-  SERIAL_ECHOPAIR(": ", *TMSK);
+  SERIAL_ECHOPGM("    TIMSK", AS_DIGIT(T), ": ", *TMSK);
 
   const uint8_t OCIE = L - 'A' + 1;
   if (N == 3) { if (WGM == 0 || WGM == 2 || WGM ==  4 || WGM ==  6) err_is_counter(); }
@@ -286,7 +281,7 @@ void timer_prefix(uint8_t T, char L, uint8_t N) {  // T - timer    L - pwm  N -
 static void pwm_details(uint8_t pin) {
   switch (digitalPinToTimer_DEBUG(pin)) {
 
-    #if defined(TCCR0A) && defined(COM0A1)
+    #if ABTEST(0)
       #ifdef TIMER0A
         #if !AVR_AT90USB1286_FAMILY  // not available in Teensyduino type IDEs
           case TIMER0A: timer_prefix(0, 'A', 3); break;
@@ -295,7 +290,7 @@ static void pwm_details(uint8_t pin) {
       case TIMER0B: timer_prefix(0, 'B', 3); break;
     #endif
 
-    #if defined(TCCR1A) && defined(COM1A1)
+    #if ABTEST(1)
       case TIMER1A: timer_prefix(1, 'A', 4); break;
       case TIMER1B: timer_prefix(1, 'B', 4); break;
       #if defined(COM1C1) && defined(TIMER1C)
@@ -303,12 +298,12 @@ static void pwm_details(uint8_t pin) {
       #endif
     #endif
 
-    #if defined(TCCR2A) && defined(COM2A1)
+    #if ABTEST(2)
       case TIMER2A: timer_prefix(2, 'A', 3); break;
       case TIMER2B: timer_prefix(2, 'B', 3); break;
     #endif
 
-    #if defined(TCCR3A) && defined(COM3A1)
+    #if ABTEST(3)
       case TIMER3A: timer_prefix(3, 'A', 4); break;
       case TIMER3B: timer_prefix(3, 'B', 4); break;
       #ifdef COM3C1
@@ -322,7 +317,7 @@ static void pwm_details(uint8_t pin) {
       case TIMER4C: timer_prefix(4, 'C', 4); break;
     #endif
 
-    #if defined(TCCR5A) && defined(COM5A1)
+    #if ABTEST(5)
       case TIMER5A: timer_prefix(5, 'A', 4); break;
       case TIMER5B: timer_prefix(5, 'B', 4); break;
       case TIMER5C: timer_prefix(5, 'C', 4); break;
@@ -356,7 +351,6 @@ static void pwm_details(uint8_t pin) {
   #endif
 } // pwm_details
 
-
 #ifndef digitalRead_mod                   // Use Teensyduino's version of digitalRead - it doesn't disable the PWMs
   int digitalRead_mod(const int8_t pin) { // same as digitalRead except the PWM stop section has been removed
     const uint8_t port = digitalPinToPort_DEBUG(pin);
@@ -401,3 +395,6 @@ static void pwm_details(uint8_t pin) {
 #endif
 
 #define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0)
+#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d)  "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0)
+
+#undef ABTEST
diff --git a/Marlin/src/HAL/AVR/pinsDebug_Teensyduino.h b/Marlin/src/HAL/AVR/pinsDebug_Teensyduino.h
index 051972a861..582ae79ba7 100644
--- a/Marlin/src/HAL/AVR/pinsDebug_Teensyduino.h
+++ b/Marlin/src/HAL/AVR/pinsDebug_Teensyduino.h
@@ -2,6 +2,9 @@
  * Marlin 3D Printer Firmware
  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
diff --git a/Marlin/src/HAL/AVR/pinsDebug_plus_70.h b/Marlin/src/HAL/AVR/pinsDebug_plus_70.h
index db3fdf1f76..d9aa44c3cb 100644
--- a/Marlin/src/HAL/AVR/pinsDebug_plus_70.h
+++ b/Marlin/src/HAL/AVR/pinsDebug_plus_70.h
@@ -2,6 +2,9 @@
  * Marlin 3D Printer Firmware
  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
diff --git a/Marlin/src/HAL/AVR/spi_pins.h b/Marlin/src/HAL/AVR/spi_pins.h
index f3fa78e2bf..831972938a 100644
--- a/Marlin/src/HAL/AVR/spi_pins.h
+++ b/Marlin/src/HAL/AVR/spi_pins.h
@@ -51,15 +51,15 @@
   #define AVR_SS_PIN   16
 #endif
 
-#ifndef SCK_PIN
-  #define SCK_PIN  AVR_SCK_PIN
+#ifndef SD_SCK_PIN
+  #define SD_SCK_PIN  AVR_SCK_PIN
 #endif
-#ifndef MISO_PIN
-  #define MISO_PIN AVR_MISO_PIN
+#ifndef SD_MISO_PIN
+  #define SD_MISO_PIN AVR_MISO_PIN
 #endif
-#ifndef MOSI_PIN
-  #define MOSI_PIN AVR_MOSI_PIN
+#ifndef SD_MOSI_PIN
+  #define SD_MOSI_PIN AVR_MOSI_PIN
 #endif
-#ifndef SS_PIN
-  #define SS_PIN   AVR_SS_PIN
+#ifndef SD_SS_PIN
+  #define SD_SS_PIN   AVR_SS_PIN
 #endif
diff --git a/Marlin/src/HAL/AVR/timers.h b/Marlin/src/HAL/AVR/timers.h
index 82eb8b14b1..33c3880b6b 100644
--- a/Marlin/src/HAL/AVR/timers.h
+++ b/Marlin/src/HAL/AVR/timers.h
@@ -34,14 +34,14 @@ typedef uint16_t hal_timer_t;
 
 #define HAL_TIMER_RATE          ((F_CPU) / 8)    // i.e., 2MHz or 2.5MHz
 
-#ifndef STEP_TIMER_NUM
-  #define STEP_TIMER_NUM        1
+#ifndef MF_TIMER_STEP
+  #define MF_TIMER_STEP         1
 #endif
-#ifndef PULSE_TIMER_NUM
-  #define PULSE_TIMER_NUM       STEP_TIMER_NUM
+#ifndef MF_TIMER_PULSE
+  #define MF_TIMER_PULSE        MF_TIMER_STEP
 #endif
-#ifndef TEMP_TIMER_NUM
-  #define TEMP_TIMER_NUM        0
+#ifndef MF_TIMER_TEMP
+  #define MF_TIMER_TEMP         0
 #endif
 
 #define TEMP_TIMER_FREQUENCY    ((F_CPU) / 64.0 / 256.0)
@@ -58,13 +58,13 @@ typedef uint16_t hal_timer_t;
 #define DISABLE_STEPPER_DRIVER_INTERRUPT() CBI(TIMSK1, OCIE1A)
 #define STEPPER_ISR_ENABLED()             TEST(TIMSK1, OCIE1A)
 
-#define ENABLE_TEMPERATURE_INTERRUPT()     SBI(TIMSK0, OCIE0B)
-#define DISABLE_TEMPERATURE_INTERRUPT()    CBI(TIMSK0, OCIE0B)
-#define TEMPERATURE_ISR_ENABLED()         TEST(TIMSK0, OCIE0B)
+#define ENABLE_TEMPERATURE_INTERRUPT()     SBI(TIMSK0, OCIE0A)
+#define DISABLE_TEMPERATURE_INTERRUPT()    CBI(TIMSK0, OCIE0A)
+#define TEMPERATURE_ISR_ENABLED()         TEST(TIMSK0, OCIE0A)
 
 FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t) {
   switch (timer_num) {
-    case STEP_TIMER_NUM:
+    case MF_TIMER_STEP:
       // waveform generation = 0100 = CTC
       SET_WGM(1, CTC_OCRnA);
 
@@ -84,10 +84,10 @@ FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t) {
       TCNT1 = 0;
       break;
 
-    case TEMP_TIMER_NUM:
+    case MF_TIMER_TEMP:
       // Use timer0 for temperature measurement
       // Interleave temperature interrupt with millies interrupt
-      OCR0B = 128;
+      OCR0A = 128;
       break;
   }
 }
@@ -109,12 +109,12 @@ FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t) {
  * (otherwise, characters will be lost due to UART overflow).
  * Then: Stepper, Endstops, Temperature, and -finally- all others.
  */
-#define HAL_timer_isr_prologue(TIMER_NUM)
-#define HAL_timer_isr_epilogue(TIMER_NUM)
+#define HAL_timer_isr_prologue(T) NOOP
+#define HAL_timer_isr_epilogue(T) NOOP
 
-/* 18 cycles maximum latency */
 #ifndef HAL_STEP_TIMER_ISR
 
+/* 18 cycles maximum latency */
 #define HAL_STEP_TIMER_ISR() \
 extern "C" void TIMER1_COMPA_vect() __attribute__ ((signal, naked, used, externally_visible)); \
 extern "C" void TIMER1_COMPA_vect_bottom() asm ("TIMER1_COMPA_vect_bottom") __attribute__ ((used, externally_visible, noinline)); \
@@ -180,7 +180,7 @@ void TIMER1_COMPA_vect() { \
     :                                   \
     : [timsk0] "i" ((uint16_t)&TIMSK0), \
       [timsk1] "i" ((uint16_t)&TIMSK1), \
-      [msk0] "M" ((uint8_t)(1<
+#include 
 
-uint8_t u8g_bitData, u8g_bitNotData, u8g_bitClock, u8g_bitNotClock;
-volatile uint8_t *u8g_outData, *u8g_outClock;
+static uint8_t u8g_bitData, u8g_bitNotData, u8g_bitClock, u8g_bitNotClock;
+static volatile uint8_t *u8g_outData, *u8g_outClock;
 
 static void u8g_com_arduino_init_shift_out(uint8_t dataPin, uint8_t clockPin) {
   u8g_outData = portOutputRegister(digitalPinToPort(dataPin));
diff --git a/Marlin/src/HAL/AVR/watchdog.cpp b/Marlin/src/HAL/AVR/watchdog.cpp
deleted file mode 100644
index 3f10c4adff..0000000000
--- a/Marlin/src/HAL/AVR/watchdog.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * Marlin 3D Printer Firmware
- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
- *
- * Based on Sprinter and grbl.
- * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see .
- *
- */
-#ifdef __AVR__
-
-#include "../../inc/MarlinConfig.h"
-
-#if ENABLED(USE_WATCHDOG)
-
-#include "watchdog.h"
-
-#include "../../MarlinCore.h"
-
-// Initialize watchdog with 8s timeout, if possible. Otherwise, make it 4s.
-void watchdog_init() {
-  #if ENABLED(WATCHDOG_DURATION_8S) && defined(WDTO_8S)
-    #define WDTO_NS WDTO_8S
-  #else
-    #define WDTO_NS WDTO_4S
-  #endif
-  #if ENABLED(WATCHDOG_RESET_MANUAL)
-    // Enable the watchdog timer, but only for the interrupt.
-    // Take care, as this requires the correct order of operation, with interrupts disabled.
-    // See the datasheet of any AVR chip for details.
-    wdt_reset();
-    cli();
-    _WD_CONTROL_REG = _BV(_WD_CHANGE_BIT) | _BV(WDE);
-    _WD_CONTROL_REG = _BV(WDIE) | (WDTO_NS & 0x07) | ((WDTO_NS & 0x08) << 2); // WDTO_NS directly does not work. bit 0-2 are consecutive in the register but the highest value bit is at bit 5
-                                                                              // So worked for up to WDTO_2S
-    sei();
-    wdt_reset();
-  #else
-    wdt_enable(WDTO_NS); // The function handles the upper bit correct.
-  #endif
-  //delay(10000); // test it!
-}
-
-//===========================================================================
-//=================================== ISR ===================================
-//===========================================================================
-
-// Watchdog timer interrupt, called if main program blocks >4sec and manual reset is enabled.
-#if ENABLED(WATCHDOG_RESET_MANUAL)
-  ISR(WDT_vect) {
-    sei();  // With the interrupt driven serial we need to allow interrupts.
-    SERIAL_ERROR_MSG(STR_WATCHDOG_FIRED);
-    minkill();  // interrupt-safe final kill and infinite loop
-  }
-#endif
-
-#endif // USE_WATCHDOG
-#endif // __AVR__
diff --git a/Marlin/src/HAL/DUE/DebugMonitor.cpp b/Marlin/src/HAL/DUE/DebugMonitor.cpp
deleted file mode 100644
index 79759151d8..0000000000
--- a/Marlin/src/HAL/DUE/DebugMonitor.cpp
+++ /dev/null
@@ -1,342 +0,0 @@
-/**
- * Marlin 3D Printer Firmware
- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
- *
- * Based on Sprinter and grbl.
- * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see .
- *
- */
-#ifdef ARDUINO_ARCH_SAM
-
-#include "../../core/macros.h"
-#include "../../core/serial.h"
-
-#include "../shared/backtrace/unwinder.h"
-#include "../shared/backtrace/unwmemaccess.h"
-
-#include 
-
-// Debug monitor that dumps to the Programming port all status when
-// an exception or WDT timeout happens - And then resets the board
-
-// All the Monitor routines must run with interrupts disabled and
-// under an ISR execution context. That is why we cannot reuse the
-// Serial interrupt routines or any C runtime, as we don't know the
-// state we are when running them
-
-// A SW memory barrier, to ensure GCC does not overoptimize loops
-#define sw_barrier() __asm__ volatile("": : :"memory");
-
-// (re)initialize UART0 as a monitor output to 250000,n,8,1
-static void TXBegin() {
-
-  // Disable UART interrupt in NVIC
-  NVIC_DisableIRQ( UART_IRQn );
-
-  // We NEED memory barriers to ensure Interrupts are actually disabled!
-  // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
-  __DSB();
-  __ISB();
-
-  // Disable clock
-  pmc_disable_periph_clk( ID_UART );
-
-  // Configure PMC
-  pmc_enable_periph_clk( ID_UART );
-
-  // Disable PDC channel
-  UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
-
-  // Reset and disable receiver and transmitter
-  UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS;
-
-  // Configure mode: 8bit, No parity, 1 bit stop
-  UART->UART_MR = UART_MR_CHMODE_NORMAL | US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO;
-
-  // Configure baudrate (asynchronous, no oversampling) to BAUDRATE bauds
-  UART->UART_BRGR = (SystemCoreClock / (BAUDRATE << 4));
-
-  // Enable receiver and transmitter
-  UART->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
-}
-
-// Send character through UART with no interrupts
-static void TX(char c) {
-  while (!(UART->UART_SR & UART_SR_TXRDY)) { WDT_Restart(WDT); sw_barrier(); };
-  UART->UART_THR = c;
-}
-
-// Send String through UART
-static void TX(const char* s) {
-  while (*s) TX(*s++);
-}
-
-static void TXDigit(uint32_t d) {
-  if (d < 10) TX((char)(d+'0'));
-  else if (d < 16) TX((char)(d+'A'-10));
-  else TX('?');
-}
-
-// Send Hex number thru UART
-static void TXHex(uint32_t v) {
-  TX("0x");
-  for (uint8_t i = 0; i < 8; i++, v <<= 4)
-    TXDigit((v >> 28) & 0xF);
-}
-
-// Send Decimal number thru UART
-static void TXDec(uint32_t v) {
-  if (!v) {
-    TX('0');
-    return;
-  }
-
-  char nbrs[14];
-  char *p = &nbrs[0];
-  while (v != 0) {
-    *p++ = '0' + (v % 10);
-    v /= 10;
-  }
-  do {
-    p--;
-    TX(*p);
-  } while (p != &nbrs[0]);
-}
-
-// Dump a backtrace entry
-static bool UnwReportOut(void* ctx, const UnwReport* bte) {
-  int* p = (int*)ctx;
-
-  (*p)++;
-  TX('#'); TXDec(*p); TX(" : ");
-  TX(bte->name?bte->name:"unknown"); TX('@'); TXHex(bte->function);
-  TX('+'); TXDec(bte->address - bte->function);
-  TX(" PC:");TXHex(bte->address); TX('\n');
-  return true;
-}
-
-#ifdef UNW_DEBUG
-  void UnwPrintf(const char* format, ...) {
-    char dest[256];
-    va_list argptr;
-    va_start(argptr, format);
-    vsprintf(dest, format, argptr);
-    va_end(argptr);
-    TX(&dest[0]);
-  }
-#endif
-
-/* Table of function pointers for passing to the unwinder */
-static const UnwindCallbacks UnwCallbacks = {
-  UnwReportOut,
-  UnwReadW,
-  UnwReadH,
-  UnwReadB
-  #ifdef UNW_DEBUG
-   , UnwPrintf
-  #endif
-};
-
-/**
- * HardFaultHandler_C:
- * This is called from the HardFault_HandlerAsm with a pointer the Fault stack
- * as the parameter. We can then read the values from the stack and place them
- * into local variables for ease of reading.
- * We then read the various Fault Status and Address Registers to help decode
- * cause of the fault.
- * The function ends with a BKPT instruction to force control back into the debugger
- */
-extern "C"
-void HardFault_HandlerC(unsigned long *sp, unsigned long lr, unsigned long cause) {
-
-  static const char* causestr[] = {
-    "NMI","Hard","Mem","Bus","Usage","Debug","WDT","RSTC"
-  };
-
-  UnwindFrame btf;
-
-  // Dump report to the Programming port (interrupts are DISABLED)
-  TXBegin();
-  TX("\n\n## Software Fault detected ##\n");
-  TX("Cause: "); TX(causestr[cause]); TX('\n');
-
-  TX("R0   : "); TXHex(((unsigned long)sp[0])); TX('\n');
-  TX("R1   : "); TXHex(((unsigned long)sp[1])); TX('\n');
-  TX("R2   : "); TXHex(((unsigned long)sp[2])); TX('\n');
-  TX("R3   : "); TXHex(((unsigned long)sp[3])); TX('\n');
-  TX("R12  : "); TXHex(((unsigned long)sp[4])); TX('\n');
-  TX("LR   : "); TXHex(((unsigned long)sp[5])); TX('\n');
-  TX("PC   : "); TXHex(((unsigned long)sp[6])); TX('\n');
-  TX("PSR  : "); TXHex(((unsigned long)sp[7])); TX('\n');
-
-  // Configurable Fault Status Register
-  // Consists of MMSR, BFSR and UFSR
-  TX("CFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED28)))); TX('\n');
-
-  // Hard Fault Status Register
-  TX("HFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED2C)))); TX('\n');
-
-  // Debug Fault Status Register
-  TX("DFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED30)))); TX('\n');
-
-  // Auxiliary Fault Status Register
-  TX("AFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED3C)))); TX('\n');
-
-  // Read the Fault Address Registers. These may not contain valid values.
-  // Check BFARVALID/MMARVALID to see if they are valid values
-  // MemManage Fault Address Register
-  TX("MMAR : "); TXHex((*((volatile unsigned long *)(0xE000ED34)))); TX('\n');
-
-  // Bus Fault Address Register
-  TX("BFAR : "); TXHex((*((volatile unsigned long *)(0xE000ED38)))); TX('\n');
-
-  TX("ExcLR: "); TXHex(lr); TX('\n');
-  TX("ExcSP: "); TXHex((unsigned long)sp); TX('\n');
-
-  btf.sp = ((unsigned long)sp) + 8*4; // The original stack pointer
-  btf.fp = btf.sp;
-  btf.lr = ((unsigned long)sp[5]);
-  btf.pc = ((unsigned long)sp[6]) | 1; // Force Thumb, as CORTEX only support it
-
-  // Perform a backtrace
-  TX("\nBacktrace:\n\n");
-  int ctr = 0;
-  UnwindStart(&btf, &UnwCallbacks, &ctr);
-
-  // Disable all NVIC interrupts
-  NVIC->ICER[0] = 0xFFFFFFFF;
-  NVIC->ICER[1] = 0xFFFFFFFF;
-
-  // Relocate VTOR table to default position
-  SCB->VTOR = 0;
-
-  // Disable USB
-  otg_disable();
-
-  // Restart watchdog
-  WDT_Restart(WDT);
-
-  // Reset controller
-  NVIC_SystemReset();
-  for (;;) WDT_Restart(WDT);
-}
-
-__attribute__((naked)) void NMI_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#0")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void HardFault_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#1")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void MemManage_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#2")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void BusFault_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#3")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void UsageFault_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#4")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void DebugMon_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#5")
-    A("b HardFault_HandlerC")
-  );
-}
-
-/* This is NOT an exception, it is an interrupt handler - Nevertheless, the framing is the same */
-__attribute__((naked)) void WDT_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#6")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void RSTC_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#7")
-    A("b HardFault_HandlerC")
-  );
-}
-
-#endif // ARDUINO_ARCH_SAM
diff --git a/Marlin/src/HAL/DUE/HAL.cpp b/Marlin/src/HAL/DUE/HAL.cpp
index 6ce85a4643..4353f16497 100644
--- a/Marlin/src/HAL/DUE/HAL.cpp
+++ b/Marlin/src/HAL/DUE/HAL.cpp
@@ -25,7 +25,7 @@
 #ifdef ARDUINO_ARCH_SAM
 
 #include "../../inc/MarlinConfig.h"
-#include "HAL.h"
+#include "../../MarlinCore.h"
 
 #include 
 #include "usb/usb_task.h"
@@ -34,36 +34,33 @@
 // Public Variables
 // ------------------------
 
-uint16_t HAL_adc_result;
+uint16_t MarlinHAL::adc_result;
 
 // ------------------------
 // Public functions
 // ------------------------
 
-// HAL initialization task
-void HAL_init() {
-  // Initialize the USB stack
+#if ENABLED(POSTMORTEM_DEBUGGING)
+  extern void install_min_serial();
+#endif
+
+void MarlinHAL::init() {
   #if ENABLED(SDSUPPORT)
     OUT_WRITE(SDSS, HIGH);  // Try to set SDSS inactive before any other SPI users start up
   #endif
-  usb_task_init();
+  usb_task_init();          // Initialize the USB stack
+  TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler
 }
 
-// HAL idle task
-void HAL_idletask() {
-  // Perform USB stack housekeeping
-  usb_task_idle();
+void MarlinHAL::init_board() {
+  #ifdef BOARD_INIT
+    BOARD_INIT();
+  #endif
 }
 
-// Disable interrupts
-void cli() { noInterrupts(); }
+void MarlinHAL::idletask() { usb_task_idle(); } // Perform USB stack housekeeping
 
-// Enable interrupts
-void sei() { interrupts(); }
-
-void HAL_clear_reset_source() { }
-
-uint8_t HAL_get_reset_source() {
+uint8_t MarlinHAL::get_reset_source() {
   switch ((RSTC->RSTC_SR >> 8) & 0x07) {
     case 0: return RST_POWER_ON;
     case 1: return RST_BACKUP;
@@ -74,11 +71,105 @@ uint8_t HAL_get_reset_source() {
   }
 }
 
-void _delay_ms(const int delay_ms) {
-  // Todo: port for Due?
-  delay(delay_ms);
+void MarlinHAL::reboot() { rstc_start_software_reset(RSTC); }
+
+// ------------------------
+// Watchdog Timer
+// ------------------------
+
+#if ENABLED(USE_WATCHDOG)
+
+  // Initialize watchdog - On SAM3X, Watchdog was already configured
+  //  and enabled or disabled at startup, so no need to reconfigure it
+  //  here.
+  void MarlinHAL::watchdog_init() { WDT_Restart(WDT); } // Reset watchdog to start clean
+
+  // Reset watchdog. MUST be called at least every 4 seconds after the
+  // first watchdog_init or AVR will go into emergency procedures.
+  void MarlinHAL::watchdog_refresh() { watchdogReset(); }
+
+#endif
+
+// Override Arduino runtime to either config or disable the watchdog
+//
+// We need to configure the watchdog as soon as possible in the boot
+// process, because watchdog initialization at hardware reset on SAM3X8E
+// is unreliable, and there is risk of unintended resets if we delay
+// that initialization to a later time.
+void watchdogSetup() {
+
+  #if ENABLED(USE_WATCHDOG)
+
+    // 4 seconds timeout
+    uint32_t timeout = TERN(WATCHDOG_DURATION_8S, 8000, 4000);
+
+    // Calculate timeout value in WDT counter ticks: This assumes
+    // the slow clock is running at 32.768 kHz watchdog
+    // frequency is therefore 32768 / 128 = 256 Hz
+    timeout = (timeout << 8) / 1000;
+    if (timeout == 0)
+      timeout = 1;
+    else if (timeout > 0xFFF)
+      timeout = 0xFFF;
+
+    // We want to enable the watchdog with the specified timeout
+    uint32_t value =
+      WDT_MR_WDV(timeout) |               // With the specified timeout
+      WDT_MR_WDD(timeout) |               // and no invalid write window
+    #if !(SAMV70 || SAMV71 || SAME70 || SAMS70)
+      WDT_MR_WDRPROC   |                  // WDT fault resets processor only - We want
+                                          // to keep PIO controller state
+    #endif
+      WDT_MR_WDDBGHLT  |                  // WDT stops in debug state.
+      WDT_MR_WDIDLEHLT;                   // WDT stops in idle state.
+
+    #if ENABLED(WATCHDOG_RESET_MANUAL)
+      // We enable the watchdog timer, but only for the interrupt.
+
+      // Configure WDT to only trigger an interrupt
+      value |= WDT_MR_WDFIEN;             // Enable WDT fault interrupt.
+
+      // Disable WDT interrupt (just in case, to avoid triggering it!)
+      NVIC_DisableIRQ(WDT_IRQn);
+
+      // We NEED memory barriers to ensure Interrupts are actually disabled!
+      // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
+      __DSB();
+      __ISB();
+
+      // Initialize WDT with the given parameters
+      WDT_Enable(WDT, value);
+
+      // Configure and enable WDT interrupt.
+      NVIC_ClearPendingIRQ(WDT_IRQn);
+      NVIC_SetPriority(WDT_IRQn, 0); // Use highest priority, so we detect all kinds of lockups
+      NVIC_EnableIRQ(WDT_IRQn);
+
+    #else
+
+      // a WDT fault triggers a reset
+      value |= WDT_MR_WDRSTEN;
+
+      // Initialize WDT with the given parameters
+      WDT_Enable(WDT, value);
+
+    #endif
+
+    // Reset the watchdog
+    WDT_Restart(WDT);
+
+  #else
+
+    // Make sure to completely disable the Watchdog
+    WDT_Disable(WDT);
+
+  #endif
 }
 
+// ------------------------
+// Free Memory Accessor
+// ------------------------
+
 extern "C" {
   extern unsigned int _ebss; // end of bss section
 }
@@ -90,16 +181,21 @@ int freeMemory() {
 }
 
 // ------------------------
-// ADC
+// Serial Ports
 // ------------------------
 
-void HAL_adc_start_conversion(const uint8_t ch) {
-  HAL_adc_result = analogRead(ch);
-}
-
-uint16_t HAL_adc_get_result() {
-  // nop
-  return HAL_adc_result;
-}
+// Forward the default serial ports
+#if USING_HW_SERIAL0
+  DefaultSerial1 MSerial0(false, Serial);
+#endif
+#if USING_HW_SERIAL1
+  DefaultSerial2 MSerial1(false, Serial1);
+#endif
+#if USING_HW_SERIAL2
+  DefaultSerial3 MSerial2(false, Serial2);
+#endif
+#if USING_HW_SERIAL3
+  DefaultSerial4 MSerial3(false, Serial3);
+#endif
 
 #endif // ARDUINO_ARCH_SAM
diff --git a/Marlin/src/HAL/DUE/HAL.h b/Marlin/src/HAL/DUE/HAL.h
index 88ace59575..585b893841 100644
--- a/Marlin/src/HAL/DUE/HAL.h
+++ b/Marlin/src/HAL/DUE/HAL.h
@@ -32,105 +32,106 @@
 #include "../shared/math_32bit.h"
 #include "../shared/HAL_SPI.h"
 #include "fastio.h"
-#include "watchdog.h"
 
 #include 
 
-#define _MSERIAL(X) Serial##X
-#define MSERIAL(X) _MSERIAL(X)
-#define Serial0 Serial
+#include "../../core/serial_hook.h"
+
+// ------------------------
+// Serial ports
+// ------------------------
+
+typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1;
+typedef ForwardSerial1Class< decltype(Serial1) > DefaultSerial2;
+typedef ForwardSerial1Class< decltype(Serial2) > DefaultSerial3;
+typedef ForwardSerial1Class< decltype(Serial3) > DefaultSerial4;
+extern DefaultSerial1 MSerial0;
+extern DefaultSerial2 MSerial1;
+extern DefaultSerial3 MSerial2;
+extern DefaultSerial4 MSerial3;
+
+#define _MSERIAL(X) MSerial##X
+#define MSERIAL(X) _MSERIAL(X)
 
-// Define MYSERIAL0/1 before MarlinSerial includes!
 #if SERIAL_PORT == -1 || ENABLED(EMERGENCY_PARSER)
-  #define MYSERIAL0 customizedSerial1
+  #define MYSERIAL1 customizedSerial1
 #elif WITHIN(SERIAL_PORT, 0, 3)
-  #define MYSERIAL0 MSERIAL(SERIAL_PORT)
+  #define MYSERIAL1 MSERIAL(SERIAL_PORT)
 #else
-  #error "The required SERIAL_PORT must be from -1 to 3. Please update your configuration."
+  #error "The required SERIAL_PORT must be from 0 to 3, or -1 for USB Serial."
 #endif
 
 #ifdef SERIAL_PORT_2
   #if SERIAL_PORT_2 == -1 || ENABLED(EMERGENCY_PARSER)
-    #define MYSERIAL1 customizedSerial2
+    #define MYSERIAL2 customizedSerial2
   #elif WITHIN(SERIAL_PORT_2, 0, 3)
-    #define MYSERIAL1 MSERIAL(SERIAL_PORT_2)
+    #define MYSERIAL2 MSERIAL(SERIAL_PORT_2)
   #else
-    #error "SERIAL_PORT_2 must be from -1 to 3. Please update your configuration."
+    #error "SERIAL_PORT_2 must be from 0 to 3, or -1 for USB Serial."
+  #endif
+#endif
+
+#ifdef SERIAL_PORT_3
+  #if SERIAL_PORT_3 == -1 || ENABLED(EMERGENCY_PARSER)
+    #define MYSERIAL3 customizedSerial3
+  #elif WITHIN(SERIAL_PORT_3, 0, 3)
+    #define MYSERIAL3 MSERIAL(SERIAL_PORT_3)
+  #else
+    #error "SERIAL_PORT_3 must be from 0 to 3, or -1 for USB Serial."
+  #endif
+#endif
+
+#ifdef MMU2_SERIAL_PORT
+  #if WITHIN(MMU2_SERIAL_PORT, 0, 3)
+    #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT)
+  #else
+    #error "MMU2_SERIAL_PORT must be from 0 to 3."
   #endif
 #endif
 
 #ifdef LCD_SERIAL_PORT
-  #if LCD_SERIAL_PORT == -1
-    #define LCD_SERIAL lcdSerial
-  #elif WITHIN(LCD_SERIAL_PORT, 0, 3)
+  #if WITHIN(LCD_SERIAL_PORT, 0, 3)
     #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT)
   #else
-    #error "LCD_SERIAL_PORT must be from -1 to 3. Please update your configuration."
+    #error "LCD_SERIAL_PORT must be from 0 to 3."
   #endif
 #endif
 
 #include "MarlinSerial.h"
 #include "MarlinSerialUSB.h"
 
-// On AVR this is in math.h?
-#define square(x) ((x)*(x))
-
-#ifndef strncpy_P
-  #define strncpy_P(dest, src, num) strncpy((dest), (src), (num))
-#endif
-
-// Fix bug in pgm_read_ptr
-#undef pgm_read_ptr
-#define pgm_read_ptr(addr) (*((void**)(addr)))
-#undef pgm_read_word
-#define pgm_read_word(addr) (*((uint16_t*)(addr)))
+// ------------------------
+// Types
+// ------------------------
 
 typedef int8_t pin_t;
 
-#define SHARED_SERVOS HAS_SERVOS
-#define HAL_SERVO_LIB Servo
+#define SHARED_SERVOS HAS_SERVOS  // Use shared/servos.cpp
+
+class Servo;
+typedef Servo hal_servo_t;
 
 //
 // Interrupts
 //
-#define CRITICAL_SECTION_START()  uint32_t primask = __get_PRIMASK(); __disable_irq()
-#define CRITICAL_SECTION_END()    if (!primask) __enable_irq()
-#define ISRS_ENABLED() (!__get_PRIMASK())
-#define ENABLE_ISRS()  __enable_irq()
-#define DISABLE_ISRS() __disable_irq()
+#define sei() interrupts()
+#define cli() noInterrupts()
 
-void cli();                     // Disable interrupts
-void sei();                     // Enable interrupts
-
-void HAL_clear_reset_source();  // clear reset reason
-uint8_t HAL_get_reset_source(); // get reset reason
-
-inline void HAL_reboot() {}  // reboot the board or restart the bootloader
+#define CRITICAL_SECTION_START()  const bool _irqon = hal.isr_state(); hal.isr_off()
+#define CRITICAL_SECTION_END()    if (_irqon) hal.isr_on()
 
 //
 // ADC
 //
-extern uint16_t HAL_adc_result;     // result of last ADC conversion
+#define HAL_ADC_VREF         3.3
+#define HAL_ADC_RESOLUTION  10
 
 #ifndef analogInputToDigitalPin
   #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1)
 #endif
 
-#define HAL_ANALOG_SELECT(ch)
-
-inline void HAL_adc_init() {}//todo
-
-#define HAL_ADC_VREF         3.3
-#define HAL_ADC_RESOLUTION  10
-#define HAL_START_ADC(ch)   HAL_adc_start_conversion(ch)
-#define HAL_READ_ADC()      HAL_adc_result
-#define HAL_ADC_READY()     true
-
-void HAL_adc_start_conversion(const uint8_t ch);
-uint16_t HAL_adc_get_result();
-
 //
-// Pin Map
+// Pin Mapping for M42, M43, M226
 //
 #define GET_PIN_MAP_PIN(index) index
 #define GET_PIN_MAP_INDEX(pin) pin
@@ -139,23 +140,18 @@ uint16_t HAL_adc_get_result();
 //
 // Tone
 //
-void toneInit();
 void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration=0);
 void noTone(const pin_t _pin);
 
-// Enable hooks into idle and setup for HAL
-#define HAL_IDLETASK 1
-void HAL_idletask();
-void HAL_init();
-
-//
-// Utility functions
-//
-void _delay_ms(const int delay);
+// ------------------------
+// Class Utilities
+// ------------------------
 
 #pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-int freeMemory();
+#if GCC_VERSION <= 50000
+  #pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
 #pragma GCC diagnostic pop
 
 #ifdef __cplusplus
@@ -165,3 +161,73 @@ char *dtostrf(double __val, signed char __width, unsigned char __prec, char *__s
 #ifdef __cplusplus
   }
 #endif
+
+// Return free RAM between end of heap (or end bss) and whatever is current
+int freeMemory();
+
+// ------------------------
+// MarlinHAL Class
+// ------------------------
+
+class MarlinHAL {
+public:
+
+  // Earliest possible init, before setup()
+  MarlinHAL() {}
+
+  // Watchdog
+  static void watchdog_init()    IF_DISABLED(USE_WATCHDOG, {});
+  static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {});
+
+  static void init();          // Called early in setup()
+  static void init_board();    // Called less early in setup()
+  static void reboot();        // Restart the firmware
+
+  // Interrupts
+  static bool isr_state() { return !__get_PRIMASK(); }
+  static void isr_on()  { __enable_irq(); }
+  static void isr_off() { __disable_irq(); }
+
+  static void delay_ms(const int ms) { delay(ms); }
+
+  // Tasks, called from idle()
+  static void idletask();
+
+  // Reset
+  static uint8_t get_reset_source();
+  static void clear_reset_source() {}
+
+  // Free SRAM
+  static int freeMemory() { return ::freeMemory(); }
+
+  //
+  // ADC Methods
+  //
+
+  static uint16_t adc_result;
+
+  // Called by Temperature::init once at startup
+  static void adc_init() {}
+
+  // Called by Temperature::init for each sensor at startup
+  static void adc_enable(const uint8_t /*ch*/) {}
+
+  // Begin ADC sampling on the given channel. Called from Temperature::isr!
+  static void adc_start(const uint8_t ch) { adc_result = analogRead(ch); }
+
+  // Is the ADC ready for reading?
+  static bool adc_ready() { return true; }
+
+  // The current value of the ADC register
+  static uint16_t adc_value() { return adc_result; }
+
+  /**
+   * Set the PWM duty cycle for the pin to the given value.
+   * No inverting the duty cycle in this HAL.
+   * No changing the maximum size of the provided value to enable finer PWM duty control in this HAL.
+   */
+  static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) {
+    analogWrite(pin, v);
+  }
+
+};
diff --git a/Marlin/src/HAL/DUE/HAL_SPI.cpp b/Marlin/src/HAL/DUE/HAL_SPI.cpp
index 0451d8bcc4..f5bcaacee5 100644
--- a/Marlin/src/HAL/DUE/HAL_SPI.cpp
+++ b/Marlin/src/HAL/DUE/HAL_SPI.cpp
@@ -31,8 +31,6 @@
 
 /**
  * HAL for Arduino Due and compatible (SAM3X8E)
- *
- * For ARDUINO_ARCH_SAM
  */
 
 #ifdef ARDUINO_ARCH_SAM
@@ -56,8 +54,8 @@
   #pragma GCC optimize (3)
 
   typedef uint8_t (*pfnSpiTransfer)(uint8_t b);
-  typedef void    (*pfnSpiRxBlock)(uint8_t* buf, uint32_t nbyte);
-  typedef void    (*pfnSpiTxBlock)(const uint8_t* buf, uint32_t nbyte);
+  typedef void    (*pfnSpiRxBlock)(uint8_t *buf, uint32_t nbyte);
+  typedef void    (*pfnSpiTxBlock)(const uint8_t *buf, uint32_t nbyte);
 
   /* ---------------- Macros to be able to access definitions from asm */
   #define _PORT(IO) DIO ##  IO ## _WPORT
@@ -69,10 +67,10 @@
 
   // run at ~8 .. ~10Mhz - Tx version (Rx data discarded)
   static uint8_t spiTransferTx0(uint8_t bout) { // using Mode 0
-    uint32_t MOSI_PORT_PLUS30 = ((uint32_t) PORT(MOSI_PIN)) + 0x30;  /* SODR of port */
-    uint32_t MOSI_MASK = PIN_MASK(MOSI_PIN);
-    uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SCK_PIN)) + 0x30;    /* SODR of port */
-    uint32_t SCK_MASK = PIN_MASK(SCK_PIN);
+    uint32_t MOSI_PORT_PLUS30 = ((uint32_t) PORT(SD_MOSI_PIN)) + 0x30;  /* SODR of port */
+    uint32_t MOSI_MASK = PIN_MASK(SD_MOSI_PIN);
+    uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SD_SCK_PIN)) + 0x30;    /* SODR of port */
+    uint32_t SCK_MASK = PIN_MASK(SD_SCK_PIN);
     uint32_t idx = 0;
 
     /* Negate bout, as the assembler requires a negated value */
@@ -154,9 +152,9 @@
   static uint8_t spiTransferRx0(uint8_t) { // using Mode 0
     uint32_t bin = 0;
     uint32_t work = 0;
-    uint32_t BITBAND_MISO_PORT = BITBAND_ADDRESS( ((uint32_t)PORT(MISO_PIN))+0x3C, PIN_SHIFT(MISO_PIN));  /* PDSR of port in bitband area */
-    uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SCK_PIN)) + 0x30;    /* SODR of port */
-    uint32_t SCK_MASK = PIN_MASK(SCK_PIN);
+    uint32_t BITBAND_MISO_PORT = BITBAND_ADDRESS( ((uint32_t)PORT(SD_MISO_PIN))+0x3C, PIN_SHIFT(SD_MISO_PIN));  /* PDSR of port in bitband area */
+    uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SD_SCK_PIN)) + 0x30;    /* SODR of port */
+    uint32_t SCK_MASK = PIN_MASK(SD_SCK_PIN);
 
     /* The software SPI routine */
     __asm__ __volatile__(
@@ -225,36 +223,36 @@
   static uint8_t spiTransfer1(uint8_t b) { // using Mode 0
     int bits = 8;
     do {
-      WRITE(MOSI_PIN, b & 0x80);
+      WRITE(SD_MOSI_PIN, b & 0x80);
       b <<= 1;        // little setup time
 
-      WRITE(SCK_PIN, HIGH);
+      WRITE(SD_SCK_PIN, HIGH);
       DELAY_NS(125);  // 10 cycles @ 84mhz
 
-      b |= (READ(MISO_PIN) != 0);
+      b |= (READ(SD_MISO_PIN) != 0);
 
-      WRITE(SCK_PIN, LOW);
+      WRITE(SD_SCK_PIN, LOW);
       DELAY_NS(125);  // 10 cycles @ 84mhz
     } while (--bits);
     return b;
   }
 
   // all the others
-  static uint32_t spiDelayCyclesX4 = (F_CPU) / 1000000; // 4µs => 125khz
+  static uint16_t spiDelayNS = 4000; // 4000ns => 125khz
 
   static uint8_t spiTransferX(uint8_t b) { // using Mode 0
     int bits = 8;
     do {
-      WRITE(MOSI_PIN, b & 0x80);
+      WRITE(SD_MOSI_PIN, b & 0x80);
       b <<= 1; // little setup time
 
-      WRITE(SCK_PIN, HIGH);
-      __delay_4cycles(spiDelayCyclesX4);
+      WRITE(SD_SCK_PIN, HIGH);
+      DELAY_NS_VAR(spiDelayNS);
 
-      b |= (READ(MISO_PIN) != 0);
+      b |= (READ(SD_MISO_PIN) != 0);
 
-      WRITE(SCK_PIN, LOW);
-      __delay_4cycles(spiDelayCyclesX4);
+      WRITE(SD_SCK_PIN, LOW);
+      DELAY_NS_VAR(spiDelayNS);
     } while (--bits);
     return b;
   }
@@ -270,11 +268,11 @@
   static pfnSpiTransfer spiTransferTx = (pfnSpiTransfer)spiTransferX;
 
   // Block transfers run at ~8 .. ~10Mhz - Tx version (Rx data discarded)
-  static void spiTxBlock0(const uint8_t* ptr, uint32_t todo) {
-    uint32_t MOSI_PORT_PLUS30 = ((uint32_t) PORT(MOSI_PIN)) + 0x30;  /* SODR of port */
-    uint32_t MOSI_MASK = PIN_MASK(MOSI_PIN);
-    uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SCK_PIN)) + 0x30;    /* SODR of port */
-    uint32_t SCK_MASK = PIN_MASK(SCK_PIN);
+  static void spiTxBlock0(const uint8_t *ptr, uint32_t todo) {
+    uint32_t MOSI_PORT_PLUS30 = ((uint32_t) PORT(SD_MOSI_PIN)) + 0x30;  /* SODR of port */
+    uint32_t MOSI_MASK = PIN_MASK(SD_MOSI_PIN);
+    uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SD_SCK_PIN)) + 0x30;    /* SODR of port */
+    uint32_t SCK_MASK = PIN_MASK(SD_SCK_PIN);
     uint32_t work = 0;
     uint32_t txval = 0;
 
@@ -349,12 +347,12 @@
     );
   }
 
-  static void spiRxBlock0(uint8_t* ptr, uint32_t todo) {
+  static void spiRxBlock0(uint8_t *ptr, uint32_t todo) {
     uint32_t bin = 0;
     uint32_t work = 0;
-    uint32_t BITBAND_MISO_PORT = BITBAND_ADDRESS( ((uint32_t)PORT(MISO_PIN))+0x3C, PIN_SHIFT(MISO_PIN));  /* PDSR of port in bitband area */
-    uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SCK_PIN)) + 0x30;    /* SODR of port */
-    uint32_t SCK_MASK = PIN_MASK(SCK_PIN);
+    uint32_t BITBAND_MISO_PORT = BITBAND_ADDRESS( ((uint32_t)PORT(SD_MISO_PIN))+0x3C, PIN_SHIFT(SD_MISO_PIN));  /* PDSR of port in bitband area */
+    uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SD_SCK_PIN)) + 0x30;    /* SODR of port */
+    uint32_t SCK_MASK = PIN_MASK(SD_SCK_PIN);
 
     /* The software SPI routine */
     __asm__ __volatile__(
@@ -425,48 +423,48 @@
     );
   }
 
-  static void spiTxBlockX(const uint8_t* buf, uint32_t todo) {
+  static void spiTxBlockX(const uint8_t *buf, uint32_t todo) {
     do {
       (void)spiTransferTx(*buf++);
     } while (--todo);
   }
 
-  static void spiRxBlockX(uint8_t* buf, uint32_t todo) {
+  static void spiRxBlockX(uint8_t *buf, uint32_t todo) {
     do {
       *buf++ = spiTransferRx(0xFF);
     } while (--todo);
   }
 
-  // Pointers to generic functions for block tranfers
+  // Pointers to generic functions for block transfers
   static pfnSpiTxBlock spiTxBlock = (pfnSpiTxBlock)spiTxBlockX;
   static pfnSpiRxBlock spiRxBlock = (pfnSpiRxBlock)spiRxBlockX;
 
   #if MB(ALLIGATOR)
-    #define _SS_WRITE(S) WRITE(SS_PIN, S)
+    #define _SS_WRITE(S) WRITE(SD_SS_PIN, S)
   #else
     #define _SS_WRITE(S) NOOP
   #endif
 
   void spiBegin() {
-    SET_OUTPUT(SS_PIN);
+    SET_OUTPUT(SD_SS_PIN);
     _SS_WRITE(HIGH);
-    SET_OUTPUT(SCK_PIN);
-    SET_INPUT(MISO_PIN);
-    SET_OUTPUT(MOSI_PIN);
+    SET_OUTPUT(SD_SCK_PIN);
+    SET_INPUT(SD_MISO_PIN);
+    SET_OUTPUT(SD_MOSI_PIN);
   }
 
   uint8_t spiRec() {
     _SS_WRITE(LOW);
-    WRITE(MOSI_PIN, HIGH); // Output 1s 1
+    WRITE(SD_MOSI_PIN, HIGH); // Output 1s 1
     uint8_t b = spiTransferRx(0xFF);
     _SS_WRITE(HIGH);
     return b;
   }
 
-  void spiRead(uint8_t* buf, uint16_t nbyte) {
+  void spiRead(uint8_t *buf, uint16_t nbyte) {
     if (nbyte) {
       _SS_WRITE(LOW);
-      WRITE(MOSI_PIN, HIGH); // Output 1s 1
+      WRITE(SD_MOSI_PIN, HIGH); // Output 1s 1
       spiRxBlock(buf, nbyte);
       _SS_WRITE(HIGH);
     }
@@ -478,7 +476,7 @@
     _SS_WRITE(HIGH);
   }
 
-  void spiSendBlock(uint8_t token, const uint8_t* buf) {
+  void spiSendBlock(uint8_t token, const uint8_t *buf) {
     _SS_WRITE(LOW);
     (void)spiTransferTx(token);
     spiTxBlock(buf, 512);
@@ -510,7 +508,7 @@
         spiRxBlock = (pfnSpiRxBlock)spiRxBlockX;
         break;
       default:
-        spiDelayCyclesX4 = ((F_CPU) / 1000000) >> (6 - spiRate);
+        spiDelayNS = 4000 >> (6 - spiRate); // spiRate of 2 gives the maximum error with current CPU
         spiTransferTx = (pfnSpiTransfer)spiTransferX;
         spiTransferRx = (pfnSpiTransfer)spiTransferX;
         spiTxBlock = (pfnSpiTxBlock)spiTxBlockX;
@@ -519,8 +517,8 @@
     }
 
     _SS_WRITE(HIGH);
-    WRITE(MOSI_PIN, HIGH);
-    WRITE(SCK_PIN, LOW);
+    WRITE(SD_MOSI_PIN, HIGH);
+    WRITE(SD_SCK_PIN, LOW);
   }
 
   /** Begin SPI transaction, set clock, bit order, data mode */
@@ -575,38 +573,34 @@
 
       // Configure SPI pins
       PIO_Configure(
-         g_APinDescription[SCK_PIN].pPort,
-         g_APinDescription[SCK_PIN].ulPinType,
-         g_APinDescription[SCK_PIN].ulPin,
-         g_APinDescription[SCK_PIN].ulPinConfiguration);
+         g_APinDescription[SD_SCK_PIN].pPort,
+         g_APinDescription[SD_SCK_PIN].ulPinType,
+         g_APinDescription[SD_SCK_PIN].ulPin,
+         g_APinDescription[SD_SCK_PIN].ulPinConfiguration);
       PIO_Configure(
-         g_APinDescription[MOSI_PIN].pPort,
-         g_APinDescription[MOSI_PIN].ulPinType,
-         g_APinDescription[MOSI_PIN].ulPin,
-         g_APinDescription[MOSI_PIN].ulPinConfiguration);
+         g_APinDescription[SD_MOSI_PIN].pPort,
+         g_APinDescription[SD_MOSI_PIN].ulPinType,
+         g_APinDescription[SD_MOSI_PIN].ulPin,
+         g_APinDescription[SD_MOSI_PIN].ulPinConfiguration);
       PIO_Configure(
-         g_APinDescription[MISO_PIN].pPort,
-         g_APinDescription[MISO_PIN].ulPinType,
-         g_APinDescription[MISO_PIN].ulPin,
-         g_APinDescription[MISO_PIN].ulPinConfiguration);
+         g_APinDescription[SD_MISO_PIN].pPort,
+         g_APinDescription[SD_MISO_PIN].ulPinType,
+         g_APinDescription[SD_MISO_PIN].ulPin,
+         g_APinDescription[SD_MISO_PIN].ulPinConfiguration);
 
       // set master mode, peripheral select, fault detection
       SPI_Configure(SPI0, ID_SPI0, SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PS);
       SPI_Enable(SPI0);
 
-      SET_OUTPUT(DAC0_SYNC);
+      SET_OUTPUT(DAC0_SYNC_PIN);
       #if HAS_MULTI_EXTRUDER
-        SET_OUTPUT(DAC1_SYNC);
-        WRITE(DAC1_SYNC, HIGH);
+        OUT_WRITE(DAC1_SYNC_PIN, HIGH);
       #endif
-      SET_OUTPUT(SPI_EEPROM1_CS);
-      SET_OUTPUT(SPI_EEPROM2_CS);
-      SET_OUTPUT(SPI_FLASH_CS);
-      WRITE(DAC0_SYNC, HIGH);
-      WRITE(SPI_EEPROM1_CS, HIGH);
-      WRITE(SPI_EEPROM2_CS, HIGH);
-      WRITE(SPI_FLASH_CS, HIGH);
-      WRITE(SS_PIN, HIGH);
+      WRITE(DAC0_SYNC_PIN, HIGH);
+      OUT_WRITE(SPI_EEPROM1_CS_PIN, HIGH);
+      OUT_WRITE(SPI_EEPROM2_CS_PIN, HIGH);
+      OUT_WRITE(SPI_FLASH_CS_PIN, HIGH);
+      WRITE(SD_SS_PIN, HIGH);
 
       OUT_WRITE(SDSS, LOW);
 
@@ -645,7 +639,7 @@
     }
 
     // Read from SPI into buffer
-    void spiRead(uint8_t* buf, uint16_t nbyte) {
+    void spiRead(uint8_t *buf, uint16_t nbyte) {
       if (!nbyte) return;
       --nbyte;
       for (int i = 0; i < nbyte; i++) {
@@ -668,7 +662,7 @@
       //DELAY_US(1U);
     }
 
-    void spiSend(const uint8_t* buf, size_t nbyte) {
+    void spiSend(const uint8_t *buf, size_t nbyte) {
       if (!nbyte) return;
       --nbyte;
       for (size_t i = 0; i < nbyte; i++) {
@@ -689,7 +683,7 @@
       FLUSH_RX();
     }
 
-    void spiSend(uint32_t chan, const uint8_t* buf, size_t nbyte) {
+    void spiSend(uint32_t chan, const uint8_t *buf, size_t nbyte) {
       if (!nbyte) return;
       --nbyte;
       for (size_t i = 0; i < nbyte; i++) {
@@ -702,7 +696,7 @@
     }
 
     // Write from buffer to SPI
-    void spiSendBlock(uint8_t token, const uint8_t* buf) {
+    void spiSendBlock(uint8_t token, const uint8_t *buf) {
       SPI0->SPI_TDR = (uint32_t)token | SPI_PCS(SPI_CHAN);
       WHILE_TX(0);
       //WHILE_RX(0);
@@ -801,19 +795,19 @@
 
     uint8_t spiRec() { return (uint8_t)spiTransfer(0xFF); }
 
-    void spiRead(uint8_t* buf, uint16_t nbyte) {
+    void spiRead(uint8_t *buf, uint16_t nbyte) {
       for (int i = 0; i < nbyte; i++)
         buf[i] = spiTransfer(0xFF);
     }
 
     void spiSend(uint8_t data) { spiTransfer(data); }
 
-    void spiSend(const uint8_t* buf, size_t nbyte) {
+    void spiSend(const uint8_t *buf, size_t nbyte) {
       for (uint16_t i = 0; i < nbyte; i++)
         spiTransfer(buf[i]);
     }
 
-    void spiSendBlock(uint8_t token, const uint8_t* buf) {
+    void spiSendBlock(uint8_t token, const uint8_t *buf) {
       spiTransfer(token);
       for (uint16_t i = 0; i < 512; i++)
         spiTransfer(buf[i]);
diff --git a/Marlin/src/HAL/DUE/InterruptVectors.cpp b/Marlin/src/HAL/DUE/InterruptVectors.cpp
index e4e0ce99f2..70795d1c30 100644
--- a/Marlin/src/HAL/DUE/InterruptVectors.cpp
+++ b/Marlin/src/HAL/DUE/InterruptVectors.cpp
@@ -41,7 +41,7 @@
    practice, we need alignment to 256 bytes to make this work in all
    cases */
 __attribute__ ((aligned(256)))
-static DeviceVectors ram_tab = { nullptr };
+static DeviceVectors ram_tab[61] = { nullptr };
 
 /**
  * This function checks if the exception/interrupt table is already in SRAM or not.
diff --git a/Marlin/src/HAL/STM32_F4_F7/watchdog.h b/Marlin/src/HAL/DUE/MarlinSPI.h
similarity index 84%
rename from Marlin/src/HAL/STM32_F4_F7/watchdog.h
rename to Marlin/src/HAL/DUE/MarlinSPI.h
index 3dbc2d08c1..0c447ba4cb 100644
--- a/Marlin/src/HAL/STM32_F4_F7/watchdog.h
+++ b/Marlin/src/HAL/DUE/MarlinSPI.h
@@ -1,6 +1,6 @@
 /**
  * Marlin 3D Printer Firmware
- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  *
  * Based on Sprinter and grbl.
  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
@@ -21,7 +21,6 @@
  */
 #pragma once
 
-extern IWDG_HandleTypeDef hiwdg;
+#include 
 
-void watchdog_init();
-void HAL_watchdog_refresh();
+using MarlinSPI = SPIClass;
diff --git a/Marlin/src/HAL/DUE/MarlinSerial.cpp b/Marlin/src/HAL/DUE/MarlinSerial.cpp
index c9a372eeb1..638f7a1007 100644
--- a/Marlin/src/HAL/DUE/MarlinSerial.cpp
+++ b/Marlin/src/HAL/DUE/MarlinSerial.cpp
@@ -382,7 +382,7 @@ void MarlinSerial::flush() {
 }
 
 template
-void MarlinSerial::write(const uint8_t c) {
+size_t MarlinSerial::write(const uint8_t c) {
   _written = true;
 
   if (Cfg::TX_SIZE == 0) {
@@ -400,13 +400,13 @@ void MarlinSerial::write(const uint8_t c) {
     // XOFF char at the RX isr, but it is properly handled there
     if (!(HWUART->UART_IMR & UART_IMR_TXRDY) && (HWUART->UART_SR & UART_SR_TXRDY)) {
       HWUART->UART_THR = c;
-      return;
+      return 1;
     }
 
     const uint8_t i = (tx_buffer.head + 1) & (Cfg::TX_SIZE - 1);
 
     // If global interrupts are disabled (as the result of being called from an ISR)...
-    if (!ISRS_ENABLED()) {
+    if (!hal.isr_state()) {
 
       // Make room by polling if it is possible to transmit, and do so!
       while (i == tx_buffer.tail) {
@@ -428,6 +428,7 @@ void MarlinSerial::write(const uint8_t c) {
     // Enable TX isr - Non atomic, but it will eventually enable TX isr
     HWUART->UART_IER = UART_IER_TXRDY;
   }
+  return 1;
 }
 
 template
@@ -453,7 +454,7 @@ void MarlinSerial::flushTX() {
     if (!_written) return;
 
     // If global interrupts are disabled (as the result of being called from an ISR)...
-    if (!ISRS_ENABLED()) {
+    if (!hal.isr_state()) {
 
       // Wait until everything was transmitted - We must do polling, as interrupts are disabled
       while (tx_buffer.head != tx_buffer.tail || !(HWUART->UART_SR & UART_SR_TXEMPTY)) {
@@ -473,169 +474,21 @@ void MarlinSerial::flushTX() {
   }
 }
 
-/**
- * Imports from print.h
- */
-
-template
-void MarlinSerial::print(char c, int base) {
-  print((long)c, base);
-}
-
-template
-void MarlinSerial::print(unsigned char b, int base) {
-  print((unsigned long)b, base);
-}
-
-template
-void MarlinSerial::print(int n, int base) {
-  print((long)n, base);
-}
-
-template
-void MarlinSerial::print(unsigned int n, int base) {
-  print((unsigned long)n, base);
-}
-
-template
-void MarlinSerial::print(long n, int base) {
-  if (base == 0) write(n);
-  else if (base == 10) {
-    if (n < 0) { print('-'); n = -n; }
-    printNumber(n, 10);
-  }
-  else
-    printNumber(n, base);
-}
-
-template
-void MarlinSerial::print(unsigned long n, int base) {
-  if (base == 0) write(n);
-  else printNumber(n, base);
-}
-
-template
-void MarlinSerial::print(double n, int digits) {
-  printFloat(n, digits);
-}
-
-template
-void MarlinSerial::println() {
-  print('\r');
-  print('\n');
-}
-
-template
-void MarlinSerial::println(const String& s) {
-  print(s);
-  println();
-}
-
-template
-void MarlinSerial::println(const char c[]) {
-  print(c);
-  println();
-}
-
-template
-void MarlinSerial::println(char c, int base) {
-  print(c, base);
-  println();
-}
-
-template
-void MarlinSerial::println(unsigned char b, int base) {
-  print(b, base);
-  println();
-}
-
-template
-void MarlinSerial::println(int n, int base) {
-  print(n, base);
-  println();
-}
-
-template
-void MarlinSerial::println(unsigned int n, int base) {
-  print(n, base);
-  println();
-}
-
-template
-void MarlinSerial::println(long n, int base) {
-  print(n, base);
-  println();
-}
-
-template
-void MarlinSerial::println(unsigned long n, int base) {
-  print(n, base);
-  println();
-}
-
-template
-void MarlinSerial::println(double n, int digits) {
-  print(n, digits);
-  println();
-}
-
-// Private Methods
-template
-void MarlinSerial::printNumber(unsigned long n, uint8_t base) {
-  if (n) {
-    unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
-    int8_t i = 0;
-    while (n) {
-      buf[i++] = n % base;
-      n /= base;
-    }
-    while (i--)
-      print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10)));
-  }
-  else
-    print('0');
-}
-
-template
-void MarlinSerial::printFloat(double number, uint8_t digits) {
-  // Handle negative numbers
-  if (number < 0.0) {
-    print('-');
-    number = -number;
-  }
-
-  // Round correctly so that print(1.999, 2) prints as "2.00"
-  double rounding = 0.5;
-  LOOP_L_N(i, digits) rounding *= 0.1;
-  number += rounding;
-
-  // Extract the integer part of the number and print it
-  unsigned long int_part = (unsigned long)number;
-  double remainder = number - (double)int_part;
-  print(int_part);
-
-  // Print the decimal point, but only if there are digits beyond
-  if (digits) {
-    print('.');
-    // Extract digits from the remainder one at a time
-    while (digits--) {
-      remainder *= 10.0;
-      int toPrint = int(remainder);
-      print(toPrint);
-      remainder -= toPrint;
-    }
-  }
-}
 
 // If not using the USB port as serial port
-#if SERIAL_PORT >= 0
-  template class MarlinSerial>;      // Define
-  MarlinSerial> customizedSerial1;   // Instantiate
+#if defined(SERIAL_PORT) && SERIAL_PORT >= 0
+  template class MarlinSerial< MarlinSerialCfg >;
+  MSerialT1 customizedSerial1(MarlinSerialCfg::EMERGENCYPARSER);
 #endif
 
 #if defined(SERIAL_PORT_2) && SERIAL_PORT_2 >= 0
-  template class MarlinSerial>;    // Define
-  MarlinSerial> customizedSerial2; // Instantiate
+  template class MarlinSerial< MarlinSerialCfg >;
+  MSerialT2 customizedSerial2(MarlinSerialCfg::EMERGENCYPARSER);
+#endif
+
+#if defined(SERIAL_PORT_3) && SERIAL_PORT_3 >= 0
+  template class MarlinSerial< MarlinSerialCfg >;
+  MSerialT3 customizedSerial3(MarlinSerialCfg::EMERGENCYPARSER);
 #endif
 
 #endif // ARDUINO_ARCH_SAM
diff --git a/Marlin/src/HAL/DUE/MarlinSerial.h b/Marlin/src/HAL/DUE/MarlinSerial.h
index a194eba2f3..5a61bffee0 100644
--- a/Marlin/src/HAL/DUE/MarlinSerial.h
+++ b/Marlin/src/HAL/DUE/MarlinSerial.h
@@ -30,11 +30,7 @@
 #include 
 
 #include "../../inc/MarlinConfigPre.h"
-
-#define DEC 10
-#define HEX 16
-#define OCT 8
-#define BIN 2
+#include "../../core/serial_hook.h"
 
 // Define constants and variables for buffering incoming serial data.  We're
 // using a ring buffer (I think), in which rx_buffer_head is the index of the
@@ -119,44 +115,15 @@ public:
   static int read();
   static void flush();
   static ring_buffer_pos_t available();
-  static void write(const uint8_t c);
+  static size_t write(const uint8_t c);
   static void flushTX();
 
-  static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; }
+  static bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; }
 
   FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; }
   FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; }
   FORCE_INLINE static uint8_t framing_errors() { return Cfg::RX_FRAMING_ERRORS ? rx_framing_errors : 0; }
   FORCE_INLINE static ring_buffer_pos_t rxMaxEnqueued() { return Cfg::MAX_RX_QUEUED ? rx_max_enqueued : 0; }
-
-  FORCE_INLINE static void write(const char* str) { while (*str) write(*str++); }
-  FORCE_INLINE static void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); }
-  FORCE_INLINE static void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); }
-  FORCE_INLINE static void print(const char* str) { write(str); }
-
-  static void print(char, int = 0);
-  static void print(unsigned char, int = 0);
-  static void print(int, int = DEC);
-  static void print(unsigned int, int = DEC);
-  static void print(long, int = DEC);
-  static void print(unsigned long, int = DEC);
-  static void print(double, int = 2);
-
-  static void println(const String& s);
-  static void println(const char[]);
-  static void println(char, int = 0);
-  static void println(unsigned char, int = 0);
-  static void println(int, int = DEC);
-  static void println(unsigned int, int = DEC);
-  static void println(long, int = DEC);
-  static void println(unsigned long, int = DEC);
-  static void println(double, int = 2);
-  static void println();
-  operator bool() { return true; }
-
-private:
-  static void printNumber(unsigned long, const uint8_t);
-  static void printFloat(double, uint8_t);
 };
 
 // Serial port configuration
@@ -173,10 +140,17 @@ struct MarlinSerialCfg {
   static constexpr bool MAX_RX_QUEUED     = ENABLED(SERIAL_STATS_MAX_RX_QUEUED);
 };
 
-#if SERIAL_PORT >= 0
-  extern MarlinSerial> customizedSerial1;
+#if defined(SERIAL_PORT) && SERIAL_PORT >= 0
+  typedef Serial1Class< MarlinSerial< MarlinSerialCfg > > MSerialT1;
+  extern MSerialT1 customizedSerial1;
 #endif
 
 #if defined(SERIAL_PORT_2) && SERIAL_PORT_2 >= 0
-  extern MarlinSerial> customizedSerial2;
+  typedef Serial1Class< MarlinSerial< MarlinSerialCfg > > MSerialT2;
+  extern MSerialT2 customizedSerial2;
+#endif
+
+#if defined(SERIAL_PORT_3) && SERIAL_PORT_3 >= 0
+  typedef Serial1Class< MarlinSerial< MarlinSerialCfg > > MSerialT3;
+  extern MSerialT3 customizedSerial3;
 #endif
diff --git a/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp b/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp
index a41dbfeb7a..8de2dc7924 100644
--- a/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp
+++ b/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp
@@ -19,13 +19,13 @@
  * along with this program.  If not, see .
  *
  */
+#ifdef ARDUINO_ARCH_SAM
 
 /**
  * MarlinSerial_Due.cpp - Hardware serial library for Arduino DUE
  * Copyright (c) 2017 Eduardo José Tagle. All right reserved
  * Based on MarlinSerial for AVR, copyright (c) 2006 Nicholas Zambetti.  All right reserved.
  */
-#ifdef ARDUINO_ARCH_SAM
 
 #include "../../inc/MarlinConfig.h"
 
@@ -33,10 +33,6 @@
 
 #include "MarlinSerialUSB.h"
 
-#if ENABLED(EMERGENCY_PARSER)
-  #include "../../feature/e_parser.h"
-#endif
-
 // Imports from Atmel USB Stack/CDC implementation
 extern "C" {
   bool usb_task_cdc_isenabled();
@@ -45,15 +41,11 @@ extern "C" {
   int udi_cdc_getc();
   bool udi_cdc_is_tx_ready();
   int udi_cdc_putc(int value);
-};
+}
 
 // Pending character
 static int pending_char = -1;
 
-#if ENABLED(EMERGENCY_PARSER)
-  static EmergencyParser::State emergency_state; // = EP_RESET
-#endif
-
 // Public Methods
 void MarlinSerialUSB::begin(const long) {}
 
@@ -73,7 +65,7 @@ int MarlinSerialUSB::peek() {
 
   pending_char = udi_cdc_getc();
 
-  TERN_(EMERGENCY_PARSER, emergency_parser.update(emergency_state, (char)pending_char));
+  TERN_(EMERGENCY_PARSER, emergency_parser.update(static_cast(this)->emergency_state, (char)pending_char));
 
   return pending_char;
 }
@@ -95,29 +87,27 @@ int MarlinSerialUSB::read() {
 
   int c = udi_cdc_getc();
 
-  TERN_(EMERGENCY_PARSER, emergency_parser.update(emergency_state, (char)c));
+  TERN_(EMERGENCY_PARSER, emergency_parser.update(static_cast(this)->emergency_state, (char)c));
 
   return c;
 }
 
-bool MarlinSerialUSB::available() {
-    /* If Pending chars */
-  return pending_char >= 0 ||
-    /* or USB CDC enumerated and configured on the PC side and some
-       bytes where sent to us */
-      (usb_task_cdc_isenabled() && udi_cdc_is_rx_ready());
+int MarlinSerialUSB::available() {
+  if (pending_char > 0) return pending_char;
+  return pending_char == 0 ||
+    // or USB CDC enumerated and configured on the PC side and some bytes where sent to us */
+    (usb_task_cdc_isenabled() && udi_cdc_is_rx_ready());
 }
 
 void MarlinSerialUSB::flush() { }
-void MarlinSerialUSB::flushTX() { }
 
-void MarlinSerialUSB::write(const uint8_t c) {
+size_t MarlinSerialUSB::write(const uint8_t c) {
 
   /* Do not even bother sending anything if USB CDC is not enumerated
      or not configured on the PC side or there is no program on the PC
      listening to our messages */
   if (!usb_task_cdc_isenabled() || !usb_task_cdc_dtr_active())
-    return;
+    return 0;
 
   /* Wait until the PC has read the pending to be sent data */
   while (usb_task_cdc_isenabled() &&
@@ -129,161 +119,23 @@ void MarlinSerialUSB::write(const uint8_t c) {
      or not configured on the PC side or there is no program on the PC
      listening to our messages at this point */
   if (!usb_task_cdc_isenabled() || !usb_task_cdc_dtr_active())
-    return;
+    return 0;
 
   // Fifo full
   //  udi_cdc_signal_overrun();
   udi_cdc_putc(c);
-}
-
-/**
- * Imports from print.h
- */
-
-void MarlinSerialUSB::print(char c, int base) {
-  print((long)c, base);
-}
-
-void MarlinSerialUSB::print(unsigned char b, int base) {
-  print((unsigned long)b, base);
-}
-
-void MarlinSerialUSB::print(int n, int base) {
-  print((long)n, base);
-}
-
-void MarlinSerialUSB::print(unsigned int n, int base) {
-  print((unsigned long)n, base);
-}
-
-void MarlinSerialUSB::print(long n, int base) {
-  if (base == 0)
-    write(n);
-  else if (base == 10) {
-    if (n < 0) {
-      print('-');
-      n = -n;
-    }
-    printNumber(n, 10);
-  }
-  else
-    printNumber(n, base);
-}
-
-void MarlinSerialUSB::print(unsigned long n, int base) {
-  if (base == 0) write(n);
-  else printNumber(n, base);
-}
-
-void MarlinSerialUSB::print(double n, int digits) {
-  printFloat(n, digits);
-}
-
-void MarlinSerialUSB::println() {
-  print('\r');
-  print('\n');
-}
-
-void MarlinSerialUSB::println(const String& s) {
-  print(s);
-  println();
-}
-
-void MarlinSerialUSB::println(const char c[]) {
-  print(c);
-  println();
-}
-
-void MarlinSerialUSB::println(char c, int base) {
-  print(c, base);
-  println();
-}
-
-void MarlinSerialUSB::println(unsigned char b, int base) {
-  print(b, base);
-  println();
-}
-
-void MarlinSerialUSB::println(int n, int base) {
-  print(n, base);
-  println();
-}
-
-void MarlinSerialUSB::println(unsigned int n, int base) {
-  print(n, base);
-  println();
-}
-
-void MarlinSerialUSB::println(long n, int base) {
-  print(n, base);
-  println();
-}
-
-void MarlinSerialUSB::println(unsigned long n, int base) {
-  print(n, base);
-  println();
-}
-
-void MarlinSerialUSB::println(double n, int digits) {
-  print(n, digits);
-  println();
-}
-
-// Private Methods
-
-void MarlinSerialUSB::printNumber(unsigned long n, uint8_t base) {
-  if (n) {
-    unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
-    int8_t i = 0;
-    while (n) {
-      buf[i++] = n % base;
-      n /= base;
-    }
-    while (i--)
-      print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10)));
-  }
-  else
-    print('0');
-}
-
-void MarlinSerialUSB::printFloat(double number, uint8_t digits) {
-  // Handle negative numbers
-  if (number < 0.0) {
-    print('-');
-    number = -number;
-  }
-
-  // Round correctly so that print(1.999, 2) prints as "2.00"
-  double rounding = 0.5;
-  LOOP_L_N(i, digits)
-    rounding *= 0.1;
-
-  number += rounding;
-
-  // Extract the integer part of the number and print it
-  unsigned long int_part = (unsigned long)number;
-  double remainder = number - (double)int_part;
-  print(int_part);
-
-  // Print the decimal point, but only if there are digits beyond
-  if (digits) {
-    print('.');
-    // Extract digits from the remainder one at a time
-    while (digits--) {
-      remainder *= 10.0;
-      int toPrint = int(remainder);
-      print(toPrint);
-      remainder -= toPrint;
-    }
-  }
+  return 1;
 }
 
 // Preinstantiate
 #if SERIAL_PORT == -1
-  MarlinSerialUSB customizedSerial1;
+  MSerialT1 customizedSerial1(TERN0(EMERGENCY_PARSER, true));
 #endif
 #if SERIAL_PORT_2 == -1
-  MarlinSerialUSB customizedSerial2;
+  MSerialT2 customizedSerial2(TERN0(EMERGENCY_PARSER, true));
+#endif
+#if SERIAL_PORT_3 == -1
+  MSerialT3 customizedSerial3(TERN0(EMERGENCY_PARSER, true));
 #endif
 
 #endif // HAS_USB_SERIAL
diff --git a/Marlin/src/HAL/DUE/MarlinSerialUSB.h b/Marlin/src/HAL/DUE/MarlinSerialUSB.h
index 2e3622e553..6da1ef8c08 100644
--- a/Marlin/src/HAL/DUE/MarlinSerialUSB.h
+++ b/Marlin/src/HAL/DUE/MarlinSerialUSB.h
@@ -27,73 +27,39 @@
  */
 
 #include "../../inc/MarlinConfig.h"
-
-#if HAS_USB_SERIAL
+#include "../../core/serial_hook.h"
 
 #include 
 
-#define DEC 10
-#define HEX 16
-#define OCT 8
-#define BIN 2
-
-class MarlinSerialUSB {
-
-public:
-  MarlinSerialUSB() {};
-  static void begin(const long);
-  static void end();
-  static int peek();
-  static int read();
-  static void flush();
-  static void flushTX();
-  static bool available();
-  static void write(const uint8_t c);
+struct MarlinSerialUSB {
+  void begin(const long);
+  void end();
+  int peek();
+  int read();
+  void flush();
+  int available();
+  size_t write(const uint8_t c);
 
   #if ENABLED(SERIAL_STATS_DROPPED_RX)
-    FORCE_INLINE static uint32_t dropped() { return 0; }
+    FORCE_INLINE uint32_t dropped() { return 0; }
   #endif
 
   #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
-    FORCE_INLINE static int rxMaxEnqueued() { return 0; }
+    FORCE_INLINE int rxMaxEnqueued() { return 0; }
   #endif
-
-  FORCE_INLINE static void write(const char* str) { while (*str) write(*str++); }
-  FORCE_INLINE static void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); }
-  FORCE_INLINE static void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); }
-  FORCE_INLINE static void print(const char* str) { write(str); }
-
-  static void print(char, int = 0);
-  static void print(unsigned char, int = 0);
-  static void print(int, int = DEC);
-  static void print(unsigned int, int = DEC);
-  static void print(long, int = DEC);
-  static void print(unsigned long, int = DEC);
-  static void print(double, int = 2);
-
-  static void println(const String& s);
-  static void println(const char[]);
-  static void println(char, int = 0);
-  static void println(unsigned char, int = 0);
-  static void println(int, int = DEC);
-  static void println(unsigned int, int = DEC);
-  static void println(long, int = DEC);
-  static void println(unsigned long, int = DEC);
-  static void println(double, int = 2);
-  static void println();
-  operator bool() { return true; }
-
-private:
-  static void printNumber(unsigned long, const uint8_t);
-  static void printFloat(double, uint8_t);
 };
 
 #if SERIAL_PORT == -1
-  extern MarlinSerialUSB customizedSerial1;
+  typedef Serial1Class MSerialT1;
+  extern MSerialT1 customizedSerial1;
 #endif
 
 #if SERIAL_PORT_2 == -1
-  extern MarlinSerialUSB customizedSerial2;
+  typedef Serial1Class MSerialT2;
+  extern MSerialT2 customizedSerial2;
 #endif
 
-#endif // HAS_USB_SERIAL
+#if SERIAL_PORT_3 == -1
+  typedef Serial1Class MSerialT3;
+  extern MSerialT3 customizedSerial3;
+#endif
diff --git a/Marlin/src/HAL/DUE/MinSerial.cpp b/Marlin/src/HAL/DUE/MinSerial.cpp
new file mode 100644
index 0000000000..e5b3dbfe6f
--- /dev/null
+++ b/Marlin/src/HAL/DUE/MinSerial.cpp
@@ -0,0 +1,91 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ *
+ */
+#ifdef ARDUINO_ARCH_SAM
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(POSTMORTEM_DEBUGGING)
+
+#include "../shared/MinSerial.h"
+
+#include 
+
+static void TXBegin() {
+  // Disable UART interrupt in NVIC
+  NVIC_DisableIRQ( UART_IRQn );
+
+  // We NEED memory barriers to ensure Interrupts are actually disabled!
+  // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
+  __DSB();
+  __ISB();
+
+  // Disable clock
+  pmc_disable_periph_clk( ID_UART );
+
+  // Configure PMC
+  pmc_enable_periph_clk( ID_UART );
+
+  // Disable PDC channel
+  UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
+
+  // Reset and disable receiver and transmitter
+  UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS;
+
+  // Configure mode: 8bit, No parity, 1 bit stop
+  UART->UART_MR = UART_MR_CHMODE_NORMAL | US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO;
+
+  // Configure baudrate (asynchronous, no oversampling) to BAUDRATE bauds
+  UART->UART_BRGR = (SystemCoreClock / (BAUDRATE << 4));
+
+  // Enable receiver and transmitter
+  UART->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
+}
+
+// A SW memory barrier, to ensure GCC does not overoptimize loops
+#define sw_barrier() __asm__ volatile("": : :"memory");
+static void TX(char c) {
+  while (!(UART->UART_SR & UART_SR_TXRDY)) { WDT_Restart(WDT); sw_barrier(); };
+  UART->UART_THR = c;
+}
+
+void install_min_serial() {
+  HAL_min_serial_init = &TXBegin;
+  HAL_min_serial_out = &TX;
+}
+
+#if DISABLED(DYNAMIC_VECTORTABLE)
+extern "C" {
+  __attribute__((naked)) void JumpHandler_ASM() {
+    __asm__ __volatile__ (
+      "b CommonHandler_ASM\n"
+    );
+  }
+  void __attribute__((naked, alias("JumpHandler_ASM"))) HardFault_Handler();
+  void __attribute__((naked, alias("JumpHandler_ASM"))) BusFault_Handler();
+  void __attribute__((naked, alias("JumpHandler_ASM"))) UsageFault_Handler();
+  void __attribute__((naked, alias("JumpHandler_ASM"))) MemManage_Handler();
+  void __attribute__((naked, alias("JumpHandler_ASM"))) NMI_Handler();
+}
+#endif
+
+#endif // POSTMORTEM_DEBUGGING
+#endif // ARDUINO_ARCH_SAM
diff --git a/Marlin/src/HAL/DUE/Servo.cpp b/Marlin/src/HAL/DUE/Servo.cpp
index 5524aa9cef..2dab88238d 100644
--- a/Marlin/src/HAL/DUE/Servo.cpp
+++ b/Marlin/src/HAL/DUE/Servo.cpp
@@ -47,12 +47,12 @@
 #include "../shared/servo.h"
 #include "../shared/servo_private.h"
 
-static volatile int8_t Channel[_Nbr_16timers];              // counter for the servo being pulsed for each timer (or -1 if refresh interval)
+static Flags<_Nbr_16timers> DisablePending; // ISR should disable the timer at the next timer reset
 
 // ------------------------
 /// Interrupt handler for the TC0 channel 1.
 // ------------------------
-void Servo_Handler(timer16_Sequence_t timer, Tc *pTc, uint8_t channel);
+void Servo_Handler(const timer16_Sequence_t, Tc*, const uint8_t);
 
 #ifdef _useTimer1
   void HANDLER_FOR_TIMER1() { Servo_Handler(_timer1, TC_FOR_TIMER1, CHANNEL_FOR_TIMER1); }
@@ -70,88 +70,92 @@ void Servo_Handler(timer16_Sequence_t timer, Tc *pTc, uint8_t channel);
   void HANDLER_FOR_TIMER5() { Servo_Handler(_timer5, TC_FOR_TIMER5, CHANNEL_FOR_TIMER5); }
 #endif
 
-void Servo_Handler(timer16_Sequence_t timer, Tc *tc, uint8_t channel) {
-  // clear interrupt
-  tc->TC_CHANNEL[channel].TC_SR;
-  if (Channel[timer] < 0)
-    tc->TC_CHANNEL[channel].TC_CCR |= TC_CCR_SWTRG; // channel set to -1 indicated that refresh interval completed so reset the timer
-  else if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && SERVO(timer, Channel[timer]).Pin.isActive)
-    extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, LOW); // pulse this channel low if activated
+void Servo_Handler(const timer16_Sequence_t timer, Tc *tc, const uint8_t channel) {
+  static int8_t Channel[_Nbr_16timers];                               // Servo counters to pulse (or -1 for refresh interval)
+  int8_t cho = Channel[timer];                                        // Handle the prior Channel[timer] first
+  if (cho < 0) {                                                      // Channel -1 indicates the refresh interval completed...
+    tc->TC_CHANNEL[channel].TC_CCR |= TC_CCR_SWTRG;                   // ...so reset the timer
+    if (DisablePending[timer]) {
+      // Disabling only after the full servo period expires prevents
+      // pulses being too close together if immediately re-enabled.
+      DisablePending.clear(timer);
+      TC_Stop(tc, channel);
+      tc->TC_CHANNEL[channel].TC_SR;                                  // clear interrupt
+      return;
+    }
+  }
+  else if (SERVO_INDEX(timer, cho) < ServoCount)                      // prior channel handled?
+    extDigitalWrite(SERVO(timer, cho).Pin.nbr, LOW);                  // pulse the prior channel LOW
 
-  Channel[timer]++;    // increment to the next channel
-  if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
-    tc->TC_CHANNEL[channel].TC_RA = tc->TC_CHANNEL[channel].TC_CV + SERVO(timer,Channel[timer]).ticks;
-    if (SERVO(timer,Channel[timer]).Pin.isActive)    // check if activated
-      extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, HIGH); // its an active channel so pulse it high
+  Channel[timer] = ++cho;                                             // go to the next channel (or 0)
+  if (cho < SERVOS_PER_TIMER && SERVO_INDEX(timer, cho) < ServoCount) {
+    tc->TC_CHANNEL[channel].TC_RA = tc->TC_CHANNEL[channel].TC_CV + SERVO(timer, cho).ticks;
+    if (SERVO(timer, cho).Pin.isActive)                               // activated?
+      extDigitalWrite(SERVO(timer, cho).Pin.nbr, HIGH);               // yes: pulse HIGH
   }
   else {
     // finished all channels so wait for the refresh period to expire before starting over
-    tc->TC_CHANNEL[channel].TC_RA =
-      tc->TC_CHANNEL[channel].TC_CV < usToTicks(REFRESH_INTERVAL) - 4
-        ? (unsigned int)usToTicks(REFRESH_INTERVAL) // allow a few ticks to ensure the next OCR1A not missed
-        : tc->TC_CHANNEL[channel].TC_CV + 4;        // at least REFRESH_INTERVAL has elapsed
-    Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
+    const unsigned int cval = tc->TC_CHANNEL[channel].TC_CV + 128 / (SERVO_TIMER_PRESCALER), // allow 128 cycles to ensure the next CV not missed
+                       ival = (unsigned int)usToTicks(REFRESH_INTERVAL); // at least REFRESH_INTERVAL has elapsed
+    tc->TC_CHANNEL[channel].TC_RA = max(cval, ival);
+
+    Channel[timer] = -1;                                              // reset the timer CCR on the next call
   }
+
+  tc->TC_CHANNEL[channel].TC_SR;                                      // clear interrupt
 }
 
 static void _initISR(Tc *tc, uint32_t channel, uint32_t id, IRQn_Type irqn) {
   pmc_enable_periph_clk(id);
   TC_Configure(tc, channel,
-    TC_CMR_TCCLKS_TIMER_CLOCK3 | // MCK/32
-    TC_CMR_WAVE |                // Waveform mode
-    TC_CMR_WAVSEL_UP_RC );       // Counter running up and reset when equals to RC
+      TC_CMR_WAVE                   // Waveform mode
+    | TC_CMR_WAVSEL_UP_RC           // Counter running up and reset when equal to RC
+    | (SERVO_TIMER_PRESCALER ==   2 ? TC_CMR_TCCLKS_TIMER_CLOCK1 : 0) // MCK/2
+    | (SERVO_TIMER_PRESCALER ==   8 ? TC_CMR_TCCLKS_TIMER_CLOCK2 : 0) // MCK/8
+    | (SERVO_TIMER_PRESCALER ==  32 ? TC_CMR_TCCLKS_TIMER_CLOCK3 : 0) // MCK/32
+    | (SERVO_TIMER_PRESCALER == 128 ? TC_CMR_TCCLKS_TIMER_CLOCK4 : 0) // MCK/128
+  );
 
-  /* 84MHz, MCK/32, for 1.5ms: 3937 */
-  TC_SetRA(tc, channel, 2625); // 1ms
+  // Wait 1ms before the first ISR
+  TC_SetRA(tc, channel, (F_CPU) / (SERVO_TIMER_PRESCALER) / 1000UL); // 1ms
 
-  /* Configure and enable interrupt */
+  // Configure and enable interrupt
   NVIC_EnableIRQ(irqn);
-  // TC_IER_CPAS: RA Compare
-  tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPAS;
+  tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPAS; // TC_IER_CPAS: RA Compare
 
   // Enables the timer clock and performs a software reset to start the counting
   TC_Start(tc, channel);
 }
 
-void initISR(timer16_Sequence_t timer) {
-  #ifdef _useTimer1
-    if (timer == _timer1)
-      _initISR(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, ID_TC_FOR_TIMER1, IRQn_FOR_TIMER1);
-  #endif
-  #ifdef _useTimer2
-    if (timer == _timer2)
-      _initISR(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, ID_TC_FOR_TIMER2, IRQn_FOR_TIMER2);
-  #endif
-  #ifdef _useTimer3
-    if (timer == _timer3)
-      _initISR(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3, ID_TC_FOR_TIMER3, IRQn_FOR_TIMER3);
-  #endif
-  #ifdef _useTimer4
-    if (timer == _timer4)
-      _initISR(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4, ID_TC_FOR_TIMER4, IRQn_FOR_TIMER4);
-  #endif
-  #ifdef _useTimer5
-    if (timer == _timer5)
-      _initISR(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5, ID_TC_FOR_TIMER5, IRQn_FOR_TIMER5);
-  #endif
+void initISR(const timer16_Sequence_t timer_index) {
+  CRITICAL_SECTION_START();
+  const bool disable_soon = DisablePending[timer_index];
+  DisablePending.clear(timer_index);
+  CRITICAL_SECTION_END();
+
+  if (!disable_soon) switch (timer_index) {
+    default: break;
+    #ifdef _useTimer1
+      case _timer1: return _initISR(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, ID_TC_FOR_TIMER1, IRQn_FOR_TIMER1);
+    #endif
+    #ifdef _useTimer2
+      case _timer2: return _initISR(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, ID_TC_FOR_TIMER2, IRQn_FOR_TIMER2);
+    #endif
+    #ifdef _useTimer3
+      case _timer3: return _initISR(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3, ID_TC_FOR_TIMER3, IRQn_FOR_TIMER3);
+    #endif
+    #ifdef _useTimer4
+      case _timer4: return _initISR(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4, ID_TC_FOR_TIMER4, IRQn_FOR_TIMER4);
+    #endif
+    #ifdef _useTimer5
+      case _timer5: return _initISR(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5, ID_TC_FOR_TIMER5, IRQn_FOR_TIMER5);
+    #endif
+  }
 }
 
-void finISR(timer16_Sequence_t) {
-  #ifdef _useTimer1
-    TC_Stop(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1);
-  #endif
-  #ifdef _useTimer2
-    TC_Stop(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2);
-  #endif
-  #ifdef _useTimer3
-    TC_Stop(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3);
-  #endif
-  #ifdef _useTimer4
-    TC_Stop(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4);
-  #endif
-  #ifdef _useTimer5
-    TC_Stop(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5);
-  #endif
+void finISR(const timer16_Sequence_t timer_index) {
+  // Timer is disabled from the ISR, to ensure proper final pulse length.
+  DisablePending.set(timer_index);
 }
 
 #endif // HAS_SERVOS
diff --git a/Marlin/src/HAL/DUE/ServoTimers.h b/Marlin/src/HAL/DUE/ServoTimers.h
index c32c938253..95bd404c80 100644
--- a/Marlin/src/HAL/DUE/ServoTimers.h
+++ b/Marlin/src/HAL/DUE/ServoTimers.h
@@ -37,7 +37,7 @@
 #define _useTimer5
 
 #define TRIM_DURATION             2   // compensation ticks to trim adjust for digitalWrite delays
-#define SERVO_TIMER_PRESCALER     32  // timer prescaler
+#define SERVO_TIMER_PRESCALER     2   // timer prescaler
 
 /*
   TC0, chan 0 => TC0_Handler
diff --git a/Marlin/src/HAL/DUE/Tone.cpp b/Marlin/src/HAL/DUE/Tone.cpp
index 9beb602223..4bc8142aba 100644
--- a/Marlin/src/HAL/DUE/Tone.cpp
+++ b/Marlin/src/HAL/DUE/Tone.cpp
@@ -35,20 +35,20 @@
 static pin_t tone_pin;
 volatile static int32_t toggles;
 
-void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration) {
+void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration/*=0*/) {
   tone_pin = _pin;
   toggles = 2 * frequency * duration / 1000;
-  HAL_timer_start(TONE_TIMER_NUM, 2 * frequency);
+  HAL_timer_start(MF_TIMER_TONE, 2 * frequency);
 }
 
 void noTone(const pin_t _pin) {
-  HAL_timer_disable_interrupt(TONE_TIMER_NUM);
+  HAL_timer_disable_interrupt(MF_TIMER_TONE);
   extDigitalWrite(_pin, LOW);
 }
 
 HAL_TONE_TIMER_ISR() {
   static uint8_t pin_state = 0;
-  HAL_timer_isr_prologue(TONE_TIMER_NUM);
+  HAL_timer_isr_prologue(MF_TIMER_TONE);
 
   if (toggles) {
     toggles--;
diff --git a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_shared_hw_spi.cpp b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_shared_hw_spi.cpp
index be4b49c0f9..68f6a5c1a7 100644
--- a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_shared_hw_spi.cpp
+++ b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_shared_hw_spi.cpp
@@ -20,7 +20,6 @@
  *
  */
 
-
 /**
  * Based on u8g_com_msp430_hw_spi.c
  *
@@ -60,16 +59,15 @@
 
 #if HAS_MARLINUI_U8GLIB
 
-#include 
+#include 
 
 #include "../../../MarlinCore.h"
 
-void spiBegin();
-void spiInit(uint8_t spiRate);
-void spiSend(uint8_t b);
-void spiSend(const uint8_t* buf, size_t n);
+#ifndef LCD_SPI_SPEED
+  #define LCD_SPI_SPEED SPI_QUARTER_SPEED
+#endif
 
-#include "../../shared/Marduino.h"
+#include "../../shared/HAL_SPI.h"
 #include "../fastio.h"
 
 void u8g_SetPIOutput_DUE_hw_spi(u8g_t *u8g, uint8_t pin_index) {
@@ -100,11 +98,7 @@ uint8_t u8g_com_HAL_DUE_shared_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_va
 
       spiBegin();
 
-      #ifndef SPI_SPEED
-        #define SPI_SPEED SPI_FULL_SPEED  // use same SPI speed as SD card
-      #endif
-      spiInit(2);
-
+      spiInit(LCD_SPI_SPEED);
       break;
 
     case U8G_COM_MSG_ADDRESS:                     /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
diff --git a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_st7920_sw_spi.cpp b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_st7920_sw_spi.cpp
index 47060d6a5b..8268cf307e 100644
--- a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_st7920_sw_spi.cpp
+++ b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_st7920_sw_spi.cpp
@@ -57,11 +57,12 @@
 
 #include "../../../inc/MarlinConfigPre.h"
 
-#if ENABLED(U8GLIB_ST7920)
+#if IS_U8GLIB_ST7920
 
+#include "../../../inc/MarlinConfig.h"
 #include "../../shared/Delay.h"
 
-#include 
+#include 
 
 #include "u8g_com_HAL_DUE_sw_spi_shared.h"
 
@@ -145,7 +146,7 @@ uint8_t u8g_com_HAL_DUE_ST7920_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_va
 }
 
 #if ENABLED(LIGHTWEIGHT_UI)
-  #include "../../../lcd/ultralcd.h"
+  #include "../../../lcd/marlinui.h"
   #include "../../shared/HAL_ST7920.h"
 
   #define ST7920_CS_PIN LCD_PINS_RS
@@ -181,5 +182,5 @@ uint8_t u8g_com_HAL_DUE_ST7920_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_va
   }
 #endif // LIGHTWEIGHT_UI
 
-#endif // U8GLIB_ST7920
+#endif // IS_U8GLIB_ST7920
 #endif // ARDUINO_ARCH_SAM
diff --git a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi.cpp b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi.cpp
index ea7204fa36..68e3e74a45 100644
--- a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi.cpp
+++ b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi.cpp
@@ -57,17 +57,14 @@
 
 #include "../../../inc/MarlinConfigPre.h"
 
-#if HAS_MARLINUI_U8GLIB && DISABLED(U8GLIB_ST7920)
-
-#undef SPI_SPEED
-#define SPI_SPEED 2  // About 2 MHz
+#if HAS_MARLINUI_U8GLIB && !IS_U8GLIB_ST7920
 
 #include "u8g_com_HAL_DUE_sw_spi_shared.h"
 
 #include "../../shared/Marduino.h"
 #include "../../shared/Delay.h"
 
-#include 
+#include 
 
 #if ENABLED(FYSETC_MINI_12864)
   #define SPISEND_SW_DUE u8g_spiSend_sw_DUE_mode_3
@@ -144,5 +141,5 @@ uint8_t u8g_com_HAL_DUE_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void
   return 1;
 }
 
-#endif // HAS_MARLINUI_U8GLIB && !U8GLIB_ST7920
+#endif // HAS_MARLINUI_U8GLIB && !IS_U8GLIB_ST7920
 #endif // ARDUINO_ARCH_SAM
diff --git a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi_shared.cpp b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi_shared.cpp
index 615a386c35..904924793b 100644
--- a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi_shared.cpp
+++ b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi_shared.cpp
@@ -59,9 +59,10 @@
 
 #if HAS_MARLINUI_U8GLIB
 
+#include "../../../inc/MarlinConfig.h"
 #include "../../shared/Delay.h"
 
-#include 
+#include 
 
 #include "u8g_com_HAL_DUE_sw_spi_shared.h"
 
diff --git a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi_shared.h b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi_shared.h
index f076c503ca..45231fd091 100644
--- a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi_shared.h
+++ b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi_shared.h
@@ -23,7 +23,7 @@
 
 #include "../../../inc/MarlinConfigPre.h"
 #include "../../shared/Marduino.h"
-#include 
+#include 
 
 void u8g_SetPIOutput_DUE(u8g_t *u8g, uint8_t pin_index);
 void u8g_SetPILevel_DUE(u8g_t *u8g, uint8_t pin_index, uint8_t level);
diff --git a/Marlin/src/HAL/DUE/eeprom_flash.cpp b/Marlin/src/HAL/DUE/eeprom_flash.cpp
index 6f38da0967..607764155b 100644
--- a/Marlin/src/HAL/DUE/eeprom_flash.cpp
+++ b/Marlin/src/HAL/DUE/eeprom_flash.cpp
@@ -135,11 +135,11 @@ static uint8_t buffer[256] = {0},   // The RAM buffer to accumulate writes
 #define DEBUG_OUT ENABLED(EE_EMU_DEBUG)
 #include "../../core/debug_out.h"
 
-static void ee_Dump(const int page, const void* data) {
+static void ee_Dump(const int page, const void *data) {
 
   #ifdef EE_EMU_DEBUG
 
-    const uint8_t* c = (const uint8_t*) data;
+    const uint8_t *c = (const uint8_t*) data;
     char buffer[80];
 
     sprintf_P(buffer, PSTR("Page: %d (0x%04x)\n"), page, page);
@@ -181,7 +181,7 @@ static void ee_Dump(const int page, const void* data) {
  * @param data    (pointer to the data buffer)
  */
 __attribute__ ((long_call, section (".ramfunc")))
-static bool ee_PageWrite(uint16_t page, const void* data) {
+static bool ee_PageWrite(uint16_t page, const void *data) {
 
   uint16_t i;
   uint32_t addrflash = uint32_t(getFlashStorage(page));
@@ -199,10 +199,9 @@ static bool ee_PageWrite(uint16_t page, const void* data) {
   for (i = 0; i > 2; i++)
     pageContents[i] = (((uint32_t*)data)[i]) | (~(pageContents[i] ^ ((uint32_t*)data)[i]));
 
-  DEBUG_ECHO_START();
-  DEBUG_ECHOLNPAIR("EEPROM PageWrite   ", page);
-  DEBUG_ECHOLNPAIR(" in FLASH address ", (uint32_t)addrflash);
-  DEBUG_ECHOLNPAIR(" base address     ", (uint32_t)getFlashStorage(0));
+  DEBUG_ECHO_MSG("EEPROM PageWrite   ", page);
+  DEBUG_ECHOLNPGM(" in FLASH address ", (uint32_t)addrflash);
+  DEBUG_ECHOLNPGM(" base address     ", (uint32_t)getFlashStorage(0));
   DEBUG_FLUSH();
 
   // Get the page relative to the start of the EFC controller, and the EFC controller to use
@@ -245,8 +244,7 @@ static bool ee_PageWrite(uint16_t page, const void* data) {
     // Reenable interrupts
     __enable_irq();
 
-    DEBUG_ECHO_START();
-    DEBUG_ECHOLNPAIR("EEPROM Unlock failure for page ", page);
+    DEBUG_ECHO_MSG("EEPROM Unlock failure for page ", page);
     return false;
   }
 
@@ -270,8 +268,7 @@ static bool ee_PageWrite(uint16_t page, const void* data) {
     // Reenable interrupts
     __enable_irq();
 
-    DEBUG_ECHO_START();
-    DEBUG_ECHOLNPAIR("EEPROM Write failure for page ", page);
+    DEBUG_ECHO_MSG("EEPROM Write failure for page ", page);
 
     return false;
   }
@@ -286,15 +283,14 @@ static bool ee_PageWrite(uint16_t page, const void* data) {
   if (memcmp(getFlashStorage(page),data,PageSize)) {
 
     #ifdef EE_EMU_DEBUG
-      DEBUG_ECHO_START();
-      DEBUG_ECHOLNPAIR("EEPROM Verify Write failure for page ", page);
+      DEBUG_ECHO_MSG("EEPROM Verify Write failure for page ", page);
 
       ee_Dump( page, (uint32_t *)addrflash);
       ee_Dump(-page, data);
 
       // Calculate count of changed bits
-      uint32_t* p1 = (uint32_t*)addrflash;
-      uint32_t* p2 = (uint32_t*)data;
+      uint32_t *p1 = (uint32_t*)addrflash;
+      uint32_t *p2 = (uint32_t*)data;
       int count = 0;
       for (i =0; i> 2; i++) {
         if (p1[i] != p2[i]) {
@@ -306,7 +302,7 @@ static bool ee_PageWrite(uint16_t page, const void* data) {
           }
         }
       }
-      DEBUG_ECHOLNPAIR("--> Differing bits: ", count);
+      DEBUG_ECHOLNPGM("--> Differing bits: ", count);
     #endif
 
     return false;
@@ -325,10 +321,9 @@ static bool ee_PageErase(uint16_t page) {
   uint16_t i;
   uint32_t addrflash = uint32_t(getFlashStorage(page));
 
-  DEBUG_ECHO_START();
-  DEBUG_ECHOLNPAIR("EEPROM PageErase  ", page);
-  DEBUG_ECHOLNPAIR(" in FLASH address ", (uint32_t)addrflash);
-  DEBUG_ECHOLNPAIR(" base address     ", (uint32_t)getFlashStorage(0));
+  DEBUG_ECHO_MSG("EEPROM PageErase  ", page);
+  DEBUG_ECHOLNPGM(" in FLASH address ", (uint32_t)addrflash);
+  DEBUG_ECHOLNPGM(" base address     ", (uint32_t)getFlashStorage(0));
   DEBUG_FLUSH();
 
   // Get the page relative to the start of the EFC controller, and the EFC controller to use
@@ -370,8 +365,7 @@ static bool ee_PageErase(uint16_t page) {
     // Reenable interrupts
     __enable_irq();
 
-    DEBUG_ECHO_START();
-    DEBUG_ECHOLNPAIR("EEPROM Unlock failure for page ",page);
+    DEBUG_ECHO_MSG("EEPROM Unlock failure for page ",page);
 
     return false;
   }
@@ -394,8 +388,7 @@ static bool ee_PageErase(uint16_t page) {
     // Reenable interrupts
     __enable_irq();
 
-    DEBUG_ECHO_START();
-    DEBUG_ECHOLNPAIR("EEPROM Erase failure for page ",page);
+    DEBUG_ECHO_MSG("EEPROM Erase failure for page ",page);
 
     return false;
   }
@@ -410,8 +403,7 @@ static bool ee_PageErase(uint16_t page) {
   uint32_t * aligned_src = (uint32_t *) addrflash;
   for (i = 0; i < PageSize >> 2; i++) {
     if (*aligned_src++ != 0xFFFFFFFF) {
-      DEBUG_ECHO_START();
-      DEBUG_ECHOLNPAIR("EEPROM Verify Erase failure for page ",page);
+      DEBUG_ECHO_MSG("EEPROM Verify Erase failure for page ",page);
       ee_Dump(page, (uint32_t *)addrflash);
       return false;
     }
@@ -470,7 +462,7 @@ static uint8_t ee_Read(uint32_t address, bool excludeRAMBuffer=false) {
   for (int page = curPage - 1; page >= 0; --page) {
 
     // Get a pointer to the flash page
-    uint8_t* pflash = (uint8_t*)getFlashStorage(page + curGroup * PagesPerGroup);
+    uint8_t *pflash = (uint8_t*)getFlashStorage(page + curGroup * PagesPerGroup);
 
     uint16_t i = 0;
     while (i <= (PageSize - 4)) { /* (PageSize - 4) because otherwise, there is not enough room for data and headers */
@@ -550,7 +542,7 @@ static uint32_t ee_GetAddrRange(uint32_t address, bool excludeRAMBuffer=false) {
   for (int page = curPage - 1; page >= 0; --page) {
 
     // Get a pointer to the flash page
-    uint8_t* pflash = (uint8_t*)getFlashStorage(page + curGroup * PagesPerGroup);
+    uint8_t *pflash = (uint8_t*)getFlashStorage(page + curGroup * PagesPerGroup);
 
     uint16_t i = 0;
     while (i <= (PageSize - 4)) { /* (PageSize - 4) because otherwise, there is not enough room for data and headers */
@@ -589,7 +581,7 @@ static uint32_t ee_GetAddrRange(uint32_t address, bool excludeRAMBuffer=false) {
 }
 
 static bool ee_IsPageClean(int page) {
-  uint32_t* pflash = (uint32_t*) getFlashStorage(page);
+  uint32_t *pflash = (uint32_t*) getFlashStorage(page);
   for (uint16_t i = 0; i < (PageSize >> 2); ++i)
     if (*pflash++ != 0xFFFFFFFF) return false;
   return true;
@@ -599,7 +591,7 @@ static bool ee_Flush(uint32_t overrideAddress = 0xFFFFFFFF, uint8_t overrideData
 
   // Check if RAM buffer has something to be written
   bool isEmpty = true;
-  uint32_t* p = (uint32_t*) &buffer[0];
+  uint32_t *p = (uint32_t*) &buffer[0];
   for (uint16_t j = 0; j < (PageSize >> 2); j++) {
     if (*p++ != 0xFFFFFFFF) {
       isEmpty = false;
@@ -921,8 +913,7 @@ static void ee_Init() {
   // If all groups seem to be used, default to first group
   if (curGroup >= GroupCount) curGroup = 0;
 
-  DEBUG_ECHO_START();
-  DEBUG_ECHOLNPAIR("EEPROM Current Group: ",curGroup);
+  DEBUG_ECHO_MSG("EEPROM Current Group: ",curGroup);
   DEBUG_FLUSH();
 
   // Now, validate that all the other group pages are empty
@@ -931,8 +922,7 @@ static void ee_Init() {
 
     for (int page = 0; page < PagesPerGroup; page++) {
       if (!ee_IsPageClean(grp * PagesPerGroup + page)) {
-        DEBUG_ECHO_START();
-        DEBUG_ECHOLNPAIR("EEPROM Page ", page, " not clean on group ", grp);
+        DEBUG_ECHO_MSG("EEPROM Page ", page, " not clean on group ", grp);
         DEBUG_FLUSH();
         ee_PageErase(grp * PagesPerGroup + page);
       }
@@ -948,15 +938,13 @@ static void ee_Init() {
     }
   }
 
-  DEBUG_ECHO_START();
-  DEBUG_ECHOLNPAIR("EEPROM Active page: ", curPage);
+  DEBUG_ECHO_MSG("EEPROM Active page: ", curPage);
   DEBUG_FLUSH();
 
   // Make sure the pages following the first clean one are also clean
   for (int page = curPage + 1; page < PagesPerGroup; page++) {
     if (!ee_IsPageClean(curGroup * PagesPerGroup + page)) {
-      DEBUG_ECHO_START();
-      DEBUG_ECHOLNPAIR("EEPROM Page ", page, " not clean on active group ", curGroup);
+      DEBUG_ECHO_MSG("EEPROM Page ", page, " not clean on active group ", curGroup);
       DEBUG_FLUSH();
       ee_Dump(curGroup * PagesPerGroup + page, getFlashStorage(curGroup * PagesPerGroup + page));
       ee_PageErase(curGroup * PagesPerGroup + page);
@@ -976,14 +964,13 @@ bool PersistentStore::access_start()  { ee_Init();  return true; }
 bool PersistentStore::access_finish() { ee_Flush(); return true; }
 
 bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+  uint16_t written = 0;
   while (size--) {
     uint8_t * const p = (uint8_t * const)pos;
     uint8_t v = *value;
-    // EEPROM has only ~100,000 write cycles,
-    // so only write bytes that have changed!
-    if (v != ee_Read(uint32_t(p))) {
+    if (v != ee_Read(uint32_t(p))) { // EEPROM has only ~100,000 write cycles, so only write bytes that have changed!
       ee_Write(uint32_t(p), v);
-      delay(2);
+      if (++written & 0x7F) delay(2); else safe_delay(2); // Avoid triggering watchdog during long EEPROM writes
       if (ee_Read(uint32_t(p)) != v) {
         SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE);
         return true;
@@ -996,7 +983,7 @@ bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, ui
   return false;
 }
 
-bool PersistentStore::read_data(int &pos, uint8_t* value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
   do {
     uint8_t c = ee_Read(uint32_t(pos));
     if (writing) *value = c;
diff --git a/Marlin/src/HAL/DUE/eeprom_wired.cpp b/Marlin/src/HAL/DUE/eeprom_wired.cpp
index 4599d6a7cd..557a2f2cff 100644
--- a/Marlin/src/HAL/DUE/eeprom_wired.cpp
+++ b/Marlin/src/HAL/DUE/eeprom_wired.cpp
@@ -42,14 +42,13 @@ bool PersistentStore::access_start()  { eeprom_init(); return true; }
 bool PersistentStore::access_finish() { return true; }
 
 bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
+  uint16_t written = 0;
   while (size--) {
     uint8_t * const p = (uint8_t * const)pos;
     uint8_t v = *value;
-    // EEPROM has only ~100,000 write cycles,
-    // so only write bytes that have changed!
-    if (v != eeprom_read_byte(p)) {
+    if (v != eeprom_read_byte(p)) { // EEPROM has only ~100,000 write cycles, so only write bytes that have changed!
       eeprom_write_byte(p, v);
-      delay(2);
+      if (++written & 0x7F) delay(2); else safe_delay(2); // Avoid triggering watchdog during long EEPROM writes
       if (eeprom_read_byte(p) != v) {
         SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE);
         return true;
@@ -62,7 +61,7 @@ bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, ui
   return false;
 }
 
-bool PersistentStore::read_data(int &pos, uint8_t* value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
   do {
     uint8_t c = eeprom_read_byte((uint8_t*)pos);
     if (writing) *value = c;
diff --git a/Marlin/src/HAL/DUE/endstop_interrupts.h b/Marlin/src/HAL/DUE/endstop_interrupts.h
index 999ada5127..c1bbcb121b 100644
--- a/Marlin/src/HAL/DUE/endstop_interrupts.h
+++ b/Marlin/src/HAL/DUE/endstop_interrupts.h
@@ -64,4 +64,16 @@ void setup_endstop_interrupts() {
   TERN_(HAS_Z4_MAX, _ATTACH(Z4_MAX_PIN));
   TERN_(HAS_Z4_MIN, _ATTACH(Z4_MIN_PIN));
   TERN_(HAS_Z_MIN_PROBE_PIN, _ATTACH(Z_MIN_PROBE_PIN));
+  TERN_(HAS_I_MAX, _ATTACH(I_MAX_PIN));
+  TERN_(HAS_I_MIN, _ATTACH(I_MIN_PIN));
+  TERN_(HAS_J_MAX, _ATTACH(J_MAX_PIN));
+  TERN_(HAS_J_MIN, _ATTACH(J_MIN_PIN));
+  TERN_(HAS_K_MAX, _ATTACH(K_MAX_PIN));
+  TERN_(HAS_K_MIN, _ATTACH(K_MIN_PIN));
+  TERN_(HAS_U_MAX, _ATTACH(U_MAX_PIN));
+  TERN_(HAS_U_MIN, _ATTACH(U_MIN_PIN));
+  TERN_(HAS_V_MAX, _ATTACH(V_MAX_PIN));
+  TERN_(HAS_V_MIN, _ATTACH(V_MIN_PIN));
+  TERN_(HAS_W_MAX, _ATTACH(W_MAX_PIN));
+  TERN_(HAS_W_MIN, _ATTACH(W_MIN_PIN));
 }
diff --git a/Marlin/src/HAL/DUE/fastio.h b/Marlin/src/HAL/DUE/fastio.h
index 286319302d..a609210d81 100644
--- a/Marlin/src/HAL/DUE/fastio.h
+++ b/Marlin/src/HAL/DUE/fastio.h
@@ -33,7 +33,7 @@
  * For ARDUINO_ARCH_SAM
  * Note the code here was specifically crafted by disassembling what GCC produces
  * out of it, so GCC is able to optimize it out as much as possible to the least
- * amount of instructions. Be very carefull if you modify them, as "clean code"
+ * amount of instructions. Be very careful if you modify them, as "clean code"
  * leads to less efficient compiled code!!
  */
 
@@ -50,7 +50,7 @@
 #define PWM_PIN(P)              WITHIN(P, 2, 13)
 
 #ifndef MASK
-  #define MASK(PIN) (1 << PIN)
+  #define MASK(PIN) _BV(PIN)
 #endif
 
 /**
@@ -163,6 +163,9 @@
 #define SET_INPUT(IO)        _SET_INPUT(IO)
 // Set pin as input with pullup (wrapper)
 #define SET_INPUT_PULLUP(IO) do{ _SET_INPUT(IO); _PULLUP(IO, HIGH); }while(0)
+// Set pin as input with pulldown (substitution)
+#define SET_INPUT_PULLDOWN   SET_INPUT
+
 // Set pin as output (wrapper) -  reads the pin and sets the output to that value
 #define SET_OUTPUT(IO)       _SET_OUTPUT(IO)
 // Set pin as PWM
@@ -477,7 +480,7 @@
 #define DIO91_PIN 15
 #define DIO91_WPORT PIOB
 
-#if ARDUINO_SAM_ARCHIM
+#ifdef ARDUINO_SAM_ARCHIM
 
   #define DIO92_PIN 11
   #define DIO92_WPORT PIOC
diff --git a/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp b/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp
index d9fbabce21..800915ff69 100644
--- a/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp
+++ b/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp
@@ -25,7 +25,7 @@
  * is NOT used to directly toggle pins. The ISR writes to the pin assigned to
  * that interrupt.
  *
- * All PWMs use the same repetition rate.  The G2 needs about 10KHz min in order to
+ * All PWMs use the same repetition rate.  The G2 needs about 10kHz min in order to
  * not have obvious ripple on the Vref signals.
  *
  * The data structures are setup to minimize the computation done by the ISR which
diff --git a/Marlin/src/HAL/DUE/inc/SanityCheck.h b/Marlin/src/HAL/DUE/inc/SanityCheck.h
index 6880696506..1f5acc360c 100644
--- a/Marlin/src/HAL/DUE/inc/SanityCheck.h
+++ b/Marlin/src/HAL/DUE/inc/SanityCheck.h
@@ -25,6 +25,30 @@
  * Test Arduino Due specific configuration values for errors at compile-time.
  */
 
+/**
+ * Check for common serial pin conflicts
+ */
+#define CHECK_SERIAL_PIN(N) ( \
+     X_STOP_PIN == N || Y_STOP_PIN == N || Z_STOP_PIN == N \
+  || X_MIN_PIN  == N || Y_MIN_PIN  == N || Z_MIN_PIN  == N \
+  || X_MAX_PIN  == N || Y_MAX_PIN  == N || Z_MAX_PIN  == N \
+  || X_STEP_PIN == N || Y_STEP_PIN == N || Z_STEP_PIN == N \
+  || X_DIR_PIN  == N || Y_DIR_PIN  == N || Z_DIR_PIN  == N \
+  || X_ENA_PIN  == N || Y_ENA_PIN  == N || Z_ENA_PIN  == N \
+)
+#if SERIAL_IN_USE(0) // D0-D1. No known conflicts.
+#endif
+#if SERIAL_IN_USE(1) && (CHECK_SERIAL_PIN(18) || CHECK_SERIAL_PIN(19))
+  #error "Serial Port 1 pin D18 and/or D19 conflicts with another pin on the board."
+#endif
+#if SERIAL_IN_USE(2) && (CHECK_SERIAL_PIN(16) || CHECK_SERIAL_PIN(17))
+  #error "Serial Port 2 pin D16 and/or D17 conflicts with another pin on the board."
+#endif
+#if SERIAL_IN_USE(3) && (CHECK_SERIAL_PIN(14) || CHECK_SERIAL_PIN(15))
+  #error "Serial Port 3 pin D14 and/or D15 conflicts with another pin on the board."
+#endif
+#undef CHECK_SERIAL_PIN
+
 /**
  * HARDWARE VS. SOFTWARE SPI COMPATIBILITY
  *
@@ -40,7 +64,7 @@
  * Usually the hardware SPI pins are only available to the LCD. This makes the DUE hard SPI used at the same time
  * as the TMC2130 soft SPI the most common setup.
  */
-#define _IS_HW_SPI(P) (defined(TMC_SW_##P) && (TMC_SW_##P == MOSI_PIN || TMC_SW_##P == MISO_PIN || TMC_SW_##P == SCK_PIN))
+#define _IS_HW_SPI(P) (defined(TMC_SW_##P) && (TMC_SW_##P == SD_MOSI_PIN || TMC_SW_##P == SD_MISO_PIN || TMC_SW_##P == SD_SCK_PIN))
 
 #if ENABLED(SDSUPPORT) && HAS_DRIVER(TMC2130)
   #if ENABLED(TMC_USE_SW_SPI)
@@ -57,5 +81,9 @@
 #endif
 
 #if HAS_TMC_SW_SERIAL
-  #error "TMC220x Software Serial is not supported on this platform."
+  #error "TMC220x Software Serial is not supported on the DUE platform."
+#endif
+
+#if USING_PULLDOWNS
+  #error "PULLDOWN pin mode is not available on DUE boards."
 #endif
diff --git a/Marlin/src/HAL/DUE/pinsDebug.h b/Marlin/src/HAL/DUE/pinsDebug.h
index a99ca8ecce..2aafe9be0c 100644
--- a/Marlin/src/HAL/DUE/pinsDebug.h
+++ b/Marlin/src/HAL/DUE/pinsDebug.h
@@ -2,6 +2,9 @@
  * Marlin 3D Printer Firmware
  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
@@ -50,7 +53,7 @@
  *     The net result is that both the g_pinStatus[pin] array and the PIO_OSR register
  *     needs to be looked at when determining if a pin is an input or an output.
  *
- * b) Due has only pins 6, 7, 8 & 9 enabled for PWMs.  FYI - they run at 1KHz
+ * b) Due has only pins 6, 7, 8 & 9 enabled for PWMs.  FYI - they run at 1kHz
  *
  * c) NUM_DIGITAL_PINS does not include the analog pins
  *
@@ -64,9 +67,10 @@
 #define PRINT_PORT(p)
 #define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0)
 #define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%02d"), p); SERIAL_ECHO(buffer); }while(0)
+#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d)  "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0)
 #define GET_ARRAY_PIN(p) pin_array[p].pin
 #define GET_ARRAY_IS_DIGITAL(p) pin_array[p].is_digital
-#define VALID_PIN(pin) (pin >= 0 && pin < (int8_t)NUMBER_PINS_TOTAL ? 1 : 0)
+#define VALID_PIN(pin) (pin >= 0 && pin < int8_t(NUMBER_PINS_TOTAL))
 #define DIGITAL_PIN_TO_ANALOG_PIN(p) int(p - analogInputToDigitalPin(0))
 #define IS_ANALOG(P) WITHIN(P, char(analogInputToDigitalPin(0)), char(analogInputToDigitalPin(NUM_ANALOG_INPUTS - 1)))
 #define pwm_status(pin) (((g_pinStatus[pin] & 0xF) == PIN_STATUS_PWM) && \
@@ -82,11 +86,10 @@ bool GET_PINMODE(int8_t pin) {  // 1: output, 0: input
           || pwm_status(pin));
 }
 
-
 void pwm_details(int32_t pin) {
   if (pwm_status(pin)) {
     uint32_t chan = g_APinDescription[pin].ulPWMChannel;
-    SERIAL_ECHOPAIR("PWM = ", PWM_INTERFACE->PWM_CH_NUM[chan].PWM_CDTY);
+    SERIAL_ECHOPGM("PWM = ", PWM_INTERFACE->PWM_CH_NUM[chan].PWM_CDTY);
   }
 }
 
diff --git a/Marlin/src/HAL/DUE/spi_pins.h b/Marlin/src/HAL/DUE/spi_pins.h
index e28eaf8270..cec22c2c37 100644
--- a/Marlin/src/HAL/DUE/spi_pins.h
+++ b/Marlin/src/HAL/DUE/spi_pins.h
@@ -43,22 +43,22 @@
     #define SPI_PIN         87
     #define SPI_CHAN         1
   #endif
-  #define SCK_PIN           76
-  #define MISO_PIN          74
-  #define MOSI_PIN          75
+  #define SD_SCK_PIN        76
+  #define SD_MISO_PIN       74
+  #define SD_MOSI_PIN       75
 #else
   // defaults
   #define DUE_SOFTWARE_SPI
-  #ifndef SCK_PIN
-    #define SCK_PIN           52
+  #ifndef SD_SCK_PIN
+    #define SD_SCK_PIN      52
   #endif
-  #ifndef MISO_PIN
-    #define MISO_PIN          50
+  #ifndef SD_MISO_PIN
+    #define SD_MISO_PIN     50
   #endif
-  #ifndef MOSI_PIN
-    #define MOSI_PIN          51
+  #ifndef SD_MOSI_PIN
+    #define SD_MOSI_PIN     51
   #endif
 #endif
 
 /* A.28, A.29, B.21, C.26, C.29 */
-#define SS_PIN            SDSS
+#define SD_SS_PIN         SDSS
diff --git a/Marlin/src/HAL/DUE/timers.cpp b/Marlin/src/HAL/DUE/timers.cpp
index 9b937d1a7c..e5647817b6 100644
--- a/Marlin/src/HAL/DUE/timers.cpp
+++ b/Marlin/src/HAL/DUE/timers.cpp
@@ -42,7 +42,7 @@
 // Private Variables
 // ------------------------
 
-const tTimerConfig TimerConfig [NUM_HARDWARE_TIMERS] = {
+const tTimerConfig timer_config[NUM_HARDWARE_TIMERS] = {
   { TC0, 0, TC0_IRQn,  3}, // 0 - [servo timer5]
   { TC0, 1, TC1_IRQn,  0}, // 1
   { TC0, 2, TC2_IRQn,  2}, // 2 - stepper
@@ -66,9 +66,9 @@ const tTimerConfig TimerConfig [NUM_HARDWARE_TIMERS] = {
 */
 
 void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
-  Tc *tc = TimerConfig[timer_num].pTimerRegs;
-  IRQn_Type irq = TimerConfig[timer_num].IRQ_Id;
-  uint32_t channel = TimerConfig[timer_num].channel;
+  Tc *tc = timer_config[timer_num].pTimerRegs;
+  IRQn_Type irq = timer_config[timer_num].IRQ_Id;
+  uint32_t channel = timer_config[timer_num].channel;
 
   // Disable interrupt, just in case it was already enabled
   NVIC_DisableIRQ(irq);
@@ -86,13 +86,20 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
 
   pmc_set_writeprotect(false);
   pmc_enable_periph_clk((uint32_t)irq);
-  NVIC_SetPriority(irq, TimerConfig [timer_num].priority);
+  NVIC_SetPriority(irq, timer_config[timer_num].priority);
 
   // wave mode, reset counter on match with RC,
-  TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1);
+  TC_Configure(tc, channel,
+      TC_CMR_WAVE
+    | TC_CMR_WAVSEL_UP_RC
+    | (HAL_TIMER_PRESCALER ==   2 ? TC_CMR_TCCLKS_TIMER_CLOCK1 : 0)
+    | (HAL_TIMER_PRESCALER ==   8 ? TC_CMR_TCCLKS_TIMER_CLOCK2 : 0)
+    | (HAL_TIMER_PRESCALER ==  32 ? TC_CMR_TCCLKS_TIMER_CLOCK3 : 0)
+    | (HAL_TIMER_PRESCALER == 128 ? TC_CMR_TCCLKS_TIMER_CLOCK4 : 0)
+  );
 
   // Set compare value
-  TC_SetRC(tc, channel, VARIANT_MCK / 2 / frequency);
+  TC_SetRC(tc, channel, VARIANT_MCK / (HAL_TIMER_PRESCALER) / frequency);
 
   // And start timer
   TC_Start(tc, channel);
@@ -105,12 +112,12 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
 }
 
 void HAL_timer_enable_interrupt(const uint8_t timer_num) {
-  IRQn_Type irq = TimerConfig[timer_num].IRQ_Id;
+  IRQn_Type irq = timer_config[timer_num].IRQ_Id;
   NVIC_EnableIRQ(irq);
 }
 
 void HAL_timer_disable_interrupt(const uint8_t timer_num) {
-  IRQn_Type irq = TimerConfig[timer_num].IRQ_Id;
+  IRQn_Type irq = timer_config[timer_num].IRQ_Id;
   NVIC_DisableIRQ(irq);
 
   // We NEED memory barriers to ensure Interrupts are actually disabled!
@@ -121,11 +128,11 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num) {
 
 // missing from CMSIS: Check if interrupt is enabled or not
 static bool NVIC_GetEnabledIRQ(IRQn_Type IRQn) {
-  return (NVIC->ISER[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F))) != 0;
+  return TEST(NVIC->ISER[uint32_t(IRQn) >> 5], uint32_t(IRQn) & 0x1F);
 }
 
 bool HAL_timer_interrupt_enabled(const uint8_t timer_num) {
-  IRQn_Type irq = TimerConfig[timer_num].IRQ_Id;
+  IRQn_Type irq = timer_config[timer_num].IRQ_Id;
   return NVIC_GetEnabledIRQ(irq);
 }
 
diff --git a/Marlin/src/HAL/DUE/timers.h b/Marlin/src/HAL/DUE/timers.h
index 0e1ea07cc2..dc35c77e63 100644
--- a/Marlin/src/HAL/DUE/timers.h
+++ b/Marlin/src/HAL/DUE/timers.h
@@ -35,37 +35,38 @@
 typedef uint32_t hal_timer_t;
 #define HAL_TIMER_TYPE_MAX 0xFFFFFFFF
 
-#define HAL_TIMER_RATE         ((F_CPU) / 2)    // frequency of timers peripherals
+#define HAL_TIMER_PRESCALER    2
+#define HAL_TIMER_RATE         ((F_CPU) / (HAL_TIMER_PRESCALER))  // frequency of timers peripherals
 
-#ifndef STEP_TIMER_NUM
-  #define STEP_TIMER_NUM        2  // Timer Index for Stepper
+#ifndef MF_TIMER_STEP
+  #define MF_TIMER_STEP         2  // Timer Index for Stepper
 #endif
-#ifndef PULSE_TIMER_NUM
-  #define PULSE_TIMER_NUM       STEP_TIMER_NUM
+#ifndef MF_TIMER_PULSE
+  #define MF_TIMER_PULSE        MF_TIMER_STEP
 #endif
-#ifndef TEMP_TIMER_NUM
-  #define TEMP_TIMER_NUM        4  // Timer Index for Temperature
+#ifndef MF_TIMER_TEMP
+  #define MF_TIMER_TEMP         4  // Timer Index for Temperature
 #endif
-#ifndef TONE_TIMER_NUM
-  #define TONE_TIMER_NUM        6  // index of timer to use for beeper tones
+#ifndef MF_TIMER_TONE
+  #define MF_TIMER_TONE         6  // index of timer to use for beeper tones
 #endif
 
 #define TEMP_TIMER_FREQUENCY   1000 // temperature interrupt frequency
 
-#define STEPPER_TIMER_RATE     HAL_TIMER_RATE   // frequency of stepper timer (HAL_TIMER_RATE / STEPPER_TIMER_PRESCALE)
-#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs
-#define STEPPER_TIMER_PRESCALE (CYCLES_PER_MICROSECOND / STEPPER_TIMER_TICKS_PER_US)
+#define STEPPER_TIMER_RATE          HAL_TIMER_RATE                                        // frequency of stepper timer (HAL_TIMER_RATE / STEPPER_TIMER_PRESCALE)
+#define STEPPER_TIMER_TICKS_PER_US  ((STEPPER_TIMER_RATE) / 1000000)                      // stepper timer ticks per µs
+#define STEPPER_TIMER_PRESCALE      (CYCLES_PER_MICROSECOND / STEPPER_TIMER_TICKS_PER_US)
 
-#define PULSE_TIMER_RATE       STEPPER_TIMER_RATE   // frequency of pulse timer
-#define PULSE_TIMER_PRESCALE   STEPPER_TIMER_PRESCALE
-#define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US
+#define PULSE_TIMER_RATE            STEPPER_TIMER_RATE                                    // frequency of pulse timer
+#define PULSE_TIMER_PRESCALE        STEPPER_TIMER_PRESCALE
+#define PULSE_TIMER_TICKS_PER_US    STEPPER_TIMER_TICKS_PER_US
 
-#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(STEP_TIMER_NUM)
-#define DISABLE_STEPPER_DRIVER_INTERRUPT()  HAL_timer_disable_interrupt(STEP_TIMER_NUM)
-#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM)
+#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_STEP)
+#define DISABLE_STEPPER_DRIVER_INTERRUPT()  HAL_timer_disable_interrupt(MF_TIMER_STEP)
+#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(MF_TIMER_STEP)
 
-#define ENABLE_TEMPERATURE_INTERRUPT()  HAL_timer_enable_interrupt(TEMP_TIMER_NUM)
-#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM)
+#define ENABLE_TEMPERATURE_INTERRUPT()  HAL_timer_enable_interrupt(MF_TIMER_TEMP)
+#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_TEMP)
 
 #ifndef HAL_STEP_TIMER_ISR
   #define HAL_STEP_TIMER_ISR() void TC2_Handler()
@@ -92,7 +93,7 @@ typedef struct {
 // Public Variables
 // ------------------------
 
-extern const tTimerConfig TimerConfig[];
+extern const tTimerConfig timer_config[];
 
 // ------------------------
 // Public functions
@@ -101,17 +102,17 @@ extern const tTimerConfig TimerConfig[];
 void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency);
 
 FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) {
-  const tTimerConfig * const pConfig = &TimerConfig[timer_num];
+  const tTimerConfig * const pConfig = &timer_config[timer_num];
   pConfig->pTimerRegs->TC_CHANNEL[pConfig->channel].TC_RC = compare;
 }
 
 FORCE_INLINE static hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) {
-  const tTimerConfig * const pConfig = &TimerConfig[timer_num];
+  const tTimerConfig * const pConfig = &timer_config[timer_num];
   return pConfig->pTimerRegs->TC_CHANNEL[pConfig->channel].TC_RC;
 }
 
 FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) {
-  const tTimerConfig * const pConfig = &TimerConfig[timer_num];
+  const tTimerConfig * const pConfig = &timer_config[timer_num];
   return pConfig->pTimerRegs->TC_CHANNEL[pConfig->channel].TC_CV;
 }
 
@@ -120,9 +121,9 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num);
 bool HAL_timer_interrupt_enabled(const uint8_t timer_num);
 
 FORCE_INLINE static void HAL_timer_isr_prologue(const uint8_t timer_num) {
-  const tTimerConfig * const pConfig = &TimerConfig[timer_num];
+  const tTimerConfig * const pConfig = &timer_config[timer_num];
   // Reading the status register clears the interrupt flag
   pConfig->pTimerRegs->TC_CHANNEL[pConfig->channel].TC_SR;
 }
 
-#define HAL_timer_isr_epilogue(TIMER_NUM)
+#define HAL_timer_isr_epilogue(T) NOOP
diff --git a/Marlin/src/HAL/DUE/upload_extra_script.py b/Marlin/src/HAL/DUE/upload_extra_script.py
index d52a0a3642..ca12b3b54f 100644
--- a/Marlin/src/HAL/DUE/upload_extra_script.py
+++ b/Marlin/src/HAL/DUE/upload_extra_script.py
@@ -4,15 +4,16 @@
 #  Windows: bossac.exe
 #  Other: leave unchanged
 #
+import pioutil
+if pioutil.is_pio_build():
+    import platform
+    current_OS = platform.system()
 
-import platform
-current_OS = platform.system()
+    if current_OS == 'Windows':
 
-if current_OS == 'Windows':
+        Import("env")
 
-	Import("env")
-
-	# Use bossac.exe on Windows
-	env.Replace(
-	    UPLOADCMD="bossac --info --unlock --write --verify --reset --erase -U false --boot $SOURCE"
-	)
+        # Use bossac.exe on Windows
+        env.Replace(
+            UPLOADCMD="bossac --info --unlock --write --verify --reset --erase -U false --boot $SOURCE"
+        )
diff --git a/Marlin/src/HAL/DUE/usb/arduino_due_x.h b/Marlin/src/HAL/DUE/usb/arduino_due_x.h
index d3b333fb34..e7b6f3dcb3 100644
--- a/Marlin/src/HAL/DUE/usb/arduino_due_x.h
+++ b/Marlin/src/HAL/DUE/usb/arduino_due_x.h
@@ -71,7 +71,7 @@
 /* ------------------------------------------------------------------------ */
 
 /**
- * \page arduino_due_x_board_info "Arduino Due/X - Board informations"
+ * \page arduino_due_x_board_info "Arduino Due/X - Board information"
  * This page lists several definition related to the board description.
  *
  */
diff --git a/Marlin/src/HAL/DUE/usb/compiler.h b/Marlin/src/HAL/DUE/usb/compiler.h
index f89e554c45..633197914e 100644
--- a/Marlin/src/HAL/DUE/usb/compiler.h
+++ b/Marlin/src/HAL/DUE/usb/compiler.h
@@ -1059,7 +1059,7 @@ static inline void convert_64_bit_to_byte_array(uint64_t value, uint8_t *data)
     while (val_index < 8)
     {
         data[val_index++] = value & 0xFF;
-        value = value >> 8;
+        value >>= 8;
     }
 }
 
diff --git a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp
index ea2936359d..34cc256b30 100644
--- a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp
+++ b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp
@@ -10,7 +10,7 @@
 
 #include "../../../sd/cardreader.h"
 extern "C" {
-#include "sd_mmc_spi_mem.h"
+  #include "sd_mmc_spi_mem.h"
 }
 
 #define SD_MMC_BLOCK_SIZE 512
@@ -32,7 +32,7 @@ Ctrl_status sd_mmc_spi_test_unit_ready() {
 Ctrl_status sd_mmc_spi_read_capacity(uint32_t *nb_sector) {
   if (!IS_SD_INSERTED() || IS_SD_PRINTING() || IS_SD_FILE_OPEN() || !card.isMounted())
     return CTRL_NO_PRESENT;
-  *nb_sector = card.getSd2Card().cardSize() - 1;
+  *nb_sector = card.diskIODriver()->cardSize() - 1;
   return CTRL_GOOD;
 }
 
@@ -68,30 +68,30 @@ Ctrl_status sd_mmc_spi_usb_read_10(uint32_t addr, uint16_t nb_sector) {
   {
     char buffer[80];
     sprintf_P(buffer, PSTR("SDRD: %d @ 0x%08x\n"), nb_sector, addr);
-    PORT_REDIRECT(0);
+    PORT_REDIRECT(SERIAL_PORTMASK(0));
     SERIAL_ECHO(buffer);
   }
   #endif
 
   // Start reading
-  if (!card.getSd2Card().readStart(addr))
+  if (!card.diskIODriver()->readStart(addr))
     return CTRL_FAIL;
 
   // For each specified sector
   while (nb_sector--) {
 
     // Read a sector
-    card.getSd2Card().readData(sector_buf);
+    card.diskIODriver()->readData(sector_buf);
 
     // RAM -> USB
-    if (!udi_msc_trans_block(true, sector_buf, SD_MMC_BLOCK_SIZE, NULL)) {
-      card.getSd2Card().readStop();
+    if (!udi_msc_trans_block(true, sector_buf, SD_MMC_BLOCK_SIZE, nullptr)) {
+      card.diskIODriver()->readStop();
       return CTRL_FAIL;
     }
   }
 
   // Stop reading
-  card.getSd2Card().readStop();
+  card.diskIODriver()->readStop();
 
   // Done
   return CTRL_GOOD;
@@ -108,29 +108,29 @@ Ctrl_status sd_mmc_spi_usb_write_10(uint32_t addr, uint16_t nb_sector) {
   {
     char buffer[80];
     sprintf_P(buffer, PSTR("SDWR: %d @ 0x%08x\n"), nb_sector, addr);
-    PORT_REDIRECT(0);
+    PORT_REDIRECT(SERIAL_PORTMASK(0));
     SERIAL_ECHO(buffer);
   }
   #endif
 
-  if (!card.getSd2Card().writeStart(addr, nb_sector))
+  if (!card.diskIODriver()->writeStart(addr, nb_sector))
     return CTRL_FAIL;
 
   // For each specified sector
   while (nb_sector--) {
 
     // USB -> RAM
-    if (!udi_msc_trans_block(false, sector_buf, SD_MMC_BLOCK_SIZE, NULL)) {
-      card.getSd2Card().writeStop();
+    if (!udi_msc_trans_block(false, sector_buf, SD_MMC_BLOCK_SIZE, nullptr)) {
+      card.diskIODriver()->writeStop();
       return CTRL_FAIL;
     }
 
     // Write a sector
-    card.getSd2Card().writeData(sector_buf);
+    card.diskIODriver()->writeData(sector_buf);
   }
 
   // Stop writing
-  card.getSd2Card().writeStop();
+  card.diskIODriver()->writeStop();
 
   // Done
   return CTRL_GOOD;
diff --git a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.h b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.h
index d77e4f9523..553fd3c29a 100644
--- a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.h
+++ b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.h
@@ -74,7 +74,7 @@
 #define   SD_MMC_REMOVING      2
 
 
-//---- CONTROL FONCTIONS ----
+//---- CONTROL FUNCTIONS ----
 //!
 //! @brief This function initializes the hw/sw resources required to drive the SD_MMC_SPI.
 //!/
@@ -134,7 +134,7 @@ extern bool           sd_mmc_spi_wr_protect(void);
 extern bool           sd_mmc_spi_removal(void);
 
 
-//---- ACCESS DATA FONCTIONS ----
+//---- ACCESS DATA FUNCTIONS ----
 
 #if ACCESS_USB == true
 // Standard functions for open in read/write mode the device
diff --git a/Marlin/src/HAL/DUE/usb/udd.h b/Marlin/src/HAL/DUE/usb/udd.h
index 7ec8c03dee..319d8842f7 100644
--- a/Marlin/src/HAL/DUE/usb/udd.h
+++ b/Marlin/src/HAL/DUE/usb/udd.h
@@ -90,7 +90,7 @@ typedef struct {
 	//! This buffer must be word align for DATA IN phase (use prefix COMPILER_WORD_ALIGNED for buffer)
 	uint8_t *payload;
 
-	//! Size of buffer to send or fill, and content the number of byte transfered
+	//! Size of buffer to send or fill, and content the number of byte transferred
 	uint16_t payload_size;
 
 	//! Callback called after reception of ZLP from setup request
@@ -132,10 +132,10 @@ typedef void (*udd_callback_halt_cleared_t)(void);
  *
  * \param status     UDD_EP_TRANSFER_OK, if transfer is complete
  * \param status     UDD_EP_TRANSFER_ABORT, if transfer is aborted
- * \param n          number of data transfered
+ * \param n          number of data transferred
  */
 typedef void (*udd_callback_trans_t) (udd_ep_status_t status,
-		iram_size_t nb_transfered, udd_ep_id_t ep);
+		iram_size_t nb_transferred, udd_ep_id_t ep);
 
 /**
  * \brief Authorizes the VBUS event
@@ -303,7 +303,7 @@ bool udd_ep_wait_stall_clear(udd_ep_id_t ep,
  * The driver uses a specific DMA USB to transfer data
  * from internal RAM to endpoint, if this one is available.
  * When the transfer is finished or aborted (stall, reset, ...), the \a callback is called.
- * The \a callback returns the transfer status and eventually the number of byte transfered.
+ * The \a callback returns the transfer status and eventually the number of byte transferred.
  * Note: The control endpoint is not authorized.
  *
  * \param ep            The ID of the endpoint to use
diff --git a/Marlin/src/HAL/DUE/usb/udi_cdc.c b/Marlin/src/HAL/DUE/usb/udi_cdc.c
index cbe23dbb68..89debe57f1 100644
--- a/Marlin/src/HAL/DUE/usb/udi_cdc.c
+++ b/Marlin/src/HAL/DUE/usb/udi_cdc.c
@@ -162,7 +162,7 @@ static void udi_cdc_ctrl_state_notify(uint8_t port, udd_ep_id_t ep);
  *
  * \param status     UDD_EP_TRANSFER_OK, if transfer finished
  * \param status     UDD_EP_TRANSFER_ABORT, if transfer aborted
- * \param n          number of data transfered
+ * \param n          number of data transferred
  */
 static void udi_cdc_serial_state_msg_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep);
 
@@ -200,7 +200,7 @@ static void udi_cdc_data_received(udd_ep_status_t status, iram_size_t n, udd_ep_
  *
  * \param status     UDD_EP_TRANSFER_OK, if transfer finished
  * \param status     UDD_EP_TRANSFER_ABORT, if transfer aborted
- * \param n          number of data transfered
+ * \param n          number of data transferred
  */
 static void udi_cdc_data_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep);
 
diff --git a/Marlin/src/HAL/DUE/usb/udi_cdc.h b/Marlin/src/HAL/DUE/usb/udi_cdc.h
index 0ecf7bb00e..b61845011a 100644
--- a/Marlin/src/HAL/DUE/usb/udi_cdc.h
+++ b/Marlin/src/HAL/DUE/usb/udi_cdc.h
@@ -675,11 +675,11 @@ iram_size_t udi_cdc_multi_write_buf(uint8_t port, const void* buf, iram_size_t s
  *   - \code // Waits and gets a value on CDC line
 	int udi_cdc_getc(void);
 	// Reads a RAM buffer on CDC line
-	iram_size_t udi_cdc_read_buf(int* buf, iram_size_t size);
+	iram_size_t udi_cdc_read_buf(int *buf, iram_size_t size);
 	// Puts a byte on CDC line
 	int udi_cdc_putc(int value);
 	// Writes a RAM buffer on CDC line
-	iram_size_t udi_cdc_write_buf(const int* buf, iram_size_t size); \endcode
+	iram_size_t udi_cdc_write_buf(const int *buf, iram_size_t size); \endcode
  *
  * \section udi_cdc_use_cases Advanced use cases
  * For more advanced use of the UDI CDC module, see the following use cases:
diff --git a/Marlin/src/HAL/DUE/usb/udi_cdc_conf.h b/Marlin/src/HAL/DUE/usb/udi_cdc_conf.h
index d406a87743..e61b8cbaad 100644
--- a/Marlin/src/HAL/DUE/usb/udi_cdc_conf.h
+++ b/Marlin/src/HAL/DUE/usb/udi_cdc_conf.h
@@ -106,7 +106,7 @@ extern "C" {
  */
 //@{
 #  if UDI_CDC_PORT_NB > 2
-#    error USBB, UDP, UDPHS and UOTGHS interfaces have not enought endpoints.
+#    error USBB, UDP, UDPHS and UOTGHS interfaces have not enough endpoints.
 #  endif
 #define  UDI_CDC_DATA_EP_IN_0          (1 | USB_EP_DIR_IN)  // TX
 #define  UDI_CDC_DATA_EP_OUT_0         (2 | USB_EP_DIR_OUT) // RX
diff --git a/Marlin/src/HAL/DUE/usb/udi_msc.c b/Marlin/src/HAL/DUE/usb/udi_msc.c
index b7c3bb5ea0..dd34048772 100644
--- a/Marlin/src/HAL/DUE/usb/udi_msc.c
+++ b/Marlin/src/HAL/DUE/usb/udi_msc.c
@@ -173,7 +173,7 @@ static void udi_msc_cbw_wait(void);
  *
  * \param status       UDD_EP_TRANSFER_OK, if transfer is finished
  * \param status       UDD_EP_TRANSFER_ABORT, if transfer is aborted
- * \param nb_received  number of data transfered
+ * \param nb_received  number of data transferred
  */
 static void udi_msc_cbw_received(udd_ep_status_t status,
     iram_size_t nb_received, udd_ep_id_t ep);
@@ -211,7 +211,7 @@ static void udi_msc_data_send(uint8_t * buffer, uint8_t buf_size);
  *
  * \param status     UDD_EP_TRANSFER_OK, if transfer finish
  * \param status     UDD_EP_TRANSFER_ABORT, if transfer aborted
- * \param nb_sent    number of data transfered
+ * \param nb_sent    number of data transferred
  */
 static void udi_msc_data_sent(udd_ep_status_t status, iram_size_t nb_sent,
     udd_ep_id_t ep);
@@ -244,7 +244,7 @@ void udi_msc_csw_send(void);
  *
  * \param status     UDD_EP_TRANSFER_OK, if transfer is finished
  * \param status     UDD_EP_TRANSFER_ABORT, if transfer is aborted
- * \param nb_sent    number of data transfered
+ * \param nb_sent    number of data transferred
  */
 static void udi_msc_csw_sent(udd_ep_status_t status, iram_size_t nb_sent,
     udd_ep_id_t ep);
@@ -463,7 +463,7 @@ uint8_t udi_msc_getsetting(void)
 static void udi_msc_cbw_invalid(void)
 {
   if (!udi_msc_b_cbw_invalid)
-    return; // Don't re-stall endpoint if error reseted by setup
+    return; // Don't re-stall endpoint if error reset by setup
   udd_ep_set_halt(UDI_MSC_EP_OUT);
   // If stall cleared then re-stall it. Only Setup MSC Reset can clear it
   udd_ep_wait_stall_clear(UDI_MSC_EP_OUT, udi_msc_cbw_invalid);
@@ -472,7 +472,7 @@ static void udi_msc_cbw_invalid(void)
 static void udi_msc_csw_invalid(void)
 {
   if (!udi_msc_b_cbw_invalid)
-    return; // Don't re-stall endpoint if error reseted by setup
+    return; // Don't re-stall endpoint if error reset by setup
   udd_ep_set_halt(UDI_MSC_EP_IN);
   // If stall cleared then re-stall it. Only Setup MSC Reset can clear it
   udd_ep_wait_stall_clear(UDI_MSC_EP_IN, udi_msc_csw_invalid);
diff --git a/Marlin/src/HAL/DUE/usb/uotghs_device_due.c b/Marlin/src/HAL/DUE/usb/uotghs_device_due.c
index e13232a39c..c7e8f8d991 100644
--- a/Marlin/src/HAL/DUE/usb/uotghs_device_due.c
+++ b/Marlin/src/HAL/DUE/usb/uotghs_device_due.c
@@ -325,7 +325,7 @@ static void udd_sleep_mode(bool b_idle)
 /**
  * \name Control endpoint low level management routine.
  *
- * This function performs control endpoint mangement.
+ * This function performs control endpoint management.
  * It handle the SETUP/DATA/HANDSHAKE phases of a control transaction.
  */
 //@{
@@ -397,9 +397,9 @@ static void udd_ctrl_endofrequest(void);
 /**
  * \brief Main interrupt routine for control endpoint
  *
- * This switchs control endpoint events to correct sub function.
+ * This switches control endpoint events to correct sub function.
  *
- * \return \c 1 if an event about control endpoint is occured, otherwise \c 0.
+ * \return \c 1 if an event about control endpoint is occurred, otherwise \c 0.
  */
 static bool udd_ctrl_interrupt(void);
 
@@ -410,7 +410,7 @@ static bool udd_ctrl_interrupt(void);
  * \name Management of bulk/interrupt/isochronous endpoints
  *
  * The UDD manages the data transfer on endpoints:
- * - Start data tranfer on endpoint with USB Device DMA
+ * - Start data transfer on endpoint with USB Device DMA
  * - Send a ZLP packet if requested
  * - Call callback registered to signal end of transfer
  * The transfer abort and stall feature are supported.
@@ -431,7 +431,7 @@ typedef struct {
 	uint8_t *buf;
 	//! Size of buffer to send or fill
 	iram_size_t buf_size;
-	//!< Size of data transfered
+	//!< Size of data transferred
 	iram_size_t buf_cnt;
 	//!< Size of data loaded (or prepared for DMA) last time
 	iram_size_t buf_load;
@@ -486,7 +486,7 @@ static void udd_ep_finish_job(udd_ep_job_t * ptr_job, bool b_abort, uint8_t ep_n
 
 #ifdef UDD_EP_DMA_SUPPORTED
 	/**
-	 * \brief Start the next transfer if necessary or complet the job associated.
+	 * \brief Start the next transfer if necessary or complete the job associated.
 	 *
 	 * \param ep endpoint number without direction flag
 	 */
@@ -496,9 +496,9 @@ static void udd_ep_finish_job(udd_ep_job_t * ptr_job, bool b_abort, uint8_t ep_n
 /**
  * \brief Main interrupt routine for bulk/interrupt/isochronous endpoints
  *
- * This switchs endpoint events to correct sub function.
+ * This switches endpoint events to correct sub function.
  *
- * \return \c 1 if an event about bulk/interrupt/isochronous endpoints has occured, otherwise \c 0.
+ * \return \c 1 if an event about bulk/interrupt/isochronous endpoints has occurred, otherwise \c 0.
  */
 static bool udd_ep_interrupt(void);
 
@@ -520,7 +520,7 @@ static bool udd_ep_interrupt(void);
  *
  * Note:
  * Here, the global interrupt mask is not clear when an USB interrupt is enabled
- * because this one can not be occured during the USB ISR (=during INTX is masked).
+ * because this one can not be occurred during the USB ISR (=during INTX is masked).
  * See Technical reference $3.8.3 Masking interrupt requests in peripheral modules.
  */
 #ifdef UHD_ENABLE
@@ -787,7 +787,7 @@ void udd_attach(void)
 	udd_sleep_mode(true);
 	otg_unfreeze_clock();
 
-	// This section of clock check can be improved with a chek of
+	// This section of clock check can be improved with a check of
 	// USB clock source via sysclk()
 	// Check USB clock because the source can be a PLL
 	while (!Is_otg_clock_usable());
@@ -803,7 +803,7 @@ void udd_attach(void)
 #ifdef USB_DEVICE_HS_SUPPORT
 	udd_enable_msof_interrupt();
 #endif
-	// Reset following interupts flag
+	// Reset following interrupts flag
 	udd_ack_reset();
 	udd_ack_sof();
 	udd_ack_msof();
@@ -902,7 +902,7 @@ bool udd_ep_alloc(udd_ep_id_t ep, uint8_t bmAttributes,
 	}
 	dbg_print("alloc(%x, %d) ", ep, MaxEndpointSize);
 
-	// Bank choise
+	// Bank choice
 	switch (bmAttributes & USB_EP_TYPE_MASK) {
 	case USB_EP_TYPE_ISOCHRONOUS:
 		nb_bank = UDD_ISOCHRONOUS_NB_BANK(ep);
@@ -1228,7 +1228,7 @@ bool udd_ep_wait_stall_clear(udd_ep_id_t ep,
 
 	if (Is_udd_endpoint_stall_requested(ep)
 			|| ptr_job->stall_requested) {
-		// Endpoint halted then registes the callback
+		// Endpoint halted then registers the callback
 		ptr_job->busy = true;
 		ptr_job->call_nohalt = callback;
 	} else {
@@ -1386,7 +1386,7 @@ static void udd_ctrl_setup_received(void)
 
 	// Decode setup request
 	if (udc_process_setup() == false) {
-		// Setup request unknow then stall it
+		// Setup request unknown then stall it
 		udd_ctrl_stall_data();
 		udd_ack_setup_received(0);
 		return;
@@ -1447,7 +1447,7 @@ static void udd_ctrl_in_sent(void)
 		udd_ctrl_prev_payload_buf_cnt += udd_ctrl_payload_buf_cnt;
 		if ((udd_g_ctrlreq.req.wLength == udd_ctrl_prev_payload_buf_cnt)
 					|| b_shortpacket) {
-			// All data requested are transfered or a short packet has been sent
+			// All data requested are transferred or a short packet has been sent
 			// then it is the end of data phase.
 			// Generate an OUT ZLP for handshake phase.
 			udd_ctrl_send_zlp_out();
@@ -1516,7 +1516,7 @@ static void udd_ctrl_out_received(void)
 			// End of SETUP request:
 			// - Data IN Phase aborted,
 			// - or last Data IN Phase hidden by ZLP OUT sending quiclky,
-			// - or ZLP OUT received normaly.
+			// - or ZLP OUT received normally.
 			udd_ctrl_endofrequest();
 		} else {
 			// Protocol error during SETUP request
@@ -1544,7 +1544,7 @@ static void udd_ctrl_out_received(void)
 					(udd_ctrl_prev_payload_buf_cnt +
 							udd_ctrl_payload_buf_cnt))) {
 		// End of reception because it is a short packet
-		// Before send ZLP, call intermediat calback
+		// Before send ZLP, call intermediate callback
 		// in case of data receiv generate a stall
 		udd_g_ctrlreq.payload_size = udd_ctrl_payload_buf_cnt;
 		if (NULL != udd_g_ctrlreq.over_under_run) {
@@ -1565,7 +1565,7 @@ static void udd_ctrl_out_received(void)
 	if (udd_g_ctrlreq.payload_size == udd_ctrl_payload_buf_cnt) {
 		// Overrun then request a new payload buffer
 		if (!udd_g_ctrlreq.over_under_run) {
-			// No callback availabled to request a new payload buffer
+			// No callback available to request a new payload buffer
 			udd_ctrl_stall_data();
 			// Ack reception of OUT to replace NAK by a STALL
 			udd_ack_out_received(0);
@@ -1805,7 +1805,7 @@ static void udd_ep_trans_done(udd_ep_id_t ep)
 			// transfer size of UDD_ENDPOINT_MAX_TRANS Bytes
 			next_trans = UDD_ENDPOINT_MAX_TRANS;
 
-			// Set 0 to tranfer the maximum
+			// Set 0 to transfer the maximum
 			udd_dma_ctrl = UOTGHS_DEVDMACONTROL_BUFF_LENGTH(0);
 		} else {
 			udd_dma_ctrl = UOTGHS_DEVDMACONTROL_BUFF_LENGTH(next_trans);
@@ -1850,7 +1850,7 @@ static void udd_ep_trans_done(udd_ep_id_t ep)
 		}
 		cpu_irq_restore(flags);
 
-		// Here a ZLP has been recieved
+		// Here a ZLP has been received
 		// and the DMA transfer must be not started.
 		// It is the end of transfer
 		ptr_job->buf_size = ptr_job->buf_cnt;
@@ -1991,13 +1991,13 @@ static bool udd_ep_interrupt(void)
 			}
 			dbg_print("dma%x: ", ep);
 			udd_disable_endpoint_dma_interrupt(ep);
-			// Save number of data no transfered
+			// Save number of data no transferred
 			nb_remaining = (udd_endpoint_dma_get_status(ep) &
 					UOTGHS_DEVDMASTATUS_BUFF_COUNT_Msk)
 					>> UOTGHS_DEVDMASTATUS_BUFF_COUNT_Pos;
 			if (nb_remaining) {
 				// Transfer no complete (short packet or ZLP) then:
-				// Update number of data transfered
+				// Update number of data transferred
 				ptr_job->buf_cnt -= nb_remaining;
 				// Set transfer complete to stop the transfer
 				ptr_job->buf_size = ptr_job->buf_cnt;
@@ -2056,7 +2056,7 @@ static bool udd_ep_interrupt(void)
 				udd_disable_endpoint_interrupt(ep);
 
 				Assert(ptr_job->stall_requested);
-				// A stall has been requested during backgound transfer
+				// A stall has been requested during background transfer
 				ptr_job->stall_requested = false;
 				udd_disable_endpoint_bank_autoswitch(ep);
 				udd_enable_stall_handshake(ep);
diff --git a/Marlin/src/HAL/DUE/usb/usb_protocol_msc.h b/Marlin/src/HAL/DUE/usb/usb_protocol_msc.h
index 0fef308046..e1e59237d8 100644
--- a/Marlin/src/HAL/DUE/usb/usb_protocol_msc.h
+++ b/Marlin/src/HAL/DUE/usb/usb_protocol_msc.h
@@ -130,7 +130,7 @@ struct usb_msc_cbw {
 struct usb_msc_csw {
 	le32_t dCSWSignature;	//!< Must contain 'USBS'
 	le32_t dCSWTag;	//!< Same as dCBWTag
-	le32_t dCSWDataResidue;	//!< Number of bytes not transfered
+	le32_t dCSWDataResidue;	//!< Number of bytes not transferred
 	uint8_t bCSWStatus;	//!< Status code
 };
 
diff --git a/Marlin/src/HAL/DUE/usb/usb_task.c b/Marlin/src/HAL/DUE/usb/usb_task.c
index 66bdb265d8..86ab27217a 100644
--- a/Marlin/src/HAL/DUE/usb/usb_task.c
+++ b/Marlin/src/HAL/DUE/usb/usb_task.c
@@ -62,7 +62,7 @@ void usb_task_idle(void) {
     // Attend SD card access from the USB MSD -- Prioritize access to improve speed
     int delay = 2;
     while (main_b_msc_enable && --delay > 0) {
-      if (udi_msc_process_trans()) delay = 10000;
+      if (udi_msc_process_trans()) delay = 20;
 
       // Reset the watchdog, just to be sure
       REG_WDT_CR = WDT_CR_WDRSTT | WDT_CR_KEY(0xA5);
@@ -264,7 +264,7 @@ bool usb_task_extra_string(void) {
 ** Handle device requests that the ASF stack doesn't
 */
 bool usb_task_other_requests(void) {
-  uint8_t* ptr = 0;
+  uint8_t *ptr = 0;
   uint16_t size = 0;
 
   if (Udd_setup_type() == USB_REQ_TYPE_VENDOR) {
@@ -322,7 +322,7 @@ void usb_task_init(void) {
       char *sptr;
 
       // Patch in the filament diameter
-      sprintf_P(diam, PSTR("%d"), (int)((DEFAULT_NOMINAL_FILAMENT_DIA) * 1000.0));
+      itoa((int)((DEFAULT_NOMINAL_FILAMENT_DIA) * 1000), diam, 10);
 
       // And copy it to the proper place, expanding it to unicode
       sptr = &diam[0];
diff --git a/Marlin/src/HAL/DUE/watchdog.cpp b/Marlin/src/HAL/DUE/watchdog.cpp
deleted file mode 100644
index 0f46971830..0000000000
--- a/Marlin/src/HAL/DUE/watchdog.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- * Marlin 3D Printer Firmware
- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
- *
- * Based on Sprinter and grbl.
- * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see .
- *
- */
-#ifdef ARDUINO_ARCH_SAM
-
-#include "../../inc/MarlinConfig.h"
-#include "../../MarlinCore.h"
-#include "watchdog.h"
-
-// Override Arduino runtime to either config or disable the watchdog
-//
-// We need to configure the watchdog as soon as possible in the boot
-// process, because watchdog initialization at hardware reset on SAM3X8E
-// is unreliable, and there is risk of unintended resets if we delay
-// that initialization to a later time.
-void watchdogSetup() {
-
-  #if ENABLED(USE_WATCHDOG)
-
-    // 4 seconds timeout
-    uint32_t timeout = 4000;
-
-    // Calculate timeout value in WDT counter ticks: This assumes
-    // the slow clock is running at 32.768 kHz watchdog
-    // frequency is therefore 32768 / 128 = 256 Hz
-    timeout = (timeout << 8) / 1000;
-    if (timeout == 0)
-      timeout = 1;
-    else if (timeout > 0xFFF)
-      timeout = 0xFFF;
-
-    // We want to enable the watchdog with the specified timeout
-    uint32_t value =
-      WDT_MR_WDV(timeout) |               // With the specified timeout
-      WDT_MR_WDD(timeout) |               // and no invalid write window
-    #if !(SAMV70 || SAMV71 || SAME70 || SAMS70)
-      WDT_MR_WDRPROC   |                  // WDT fault resets processor only - We want
-                                          // to keep PIO controller state
-    #endif
-      WDT_MR_WDDBGHLT  |                  // WDT stops in debug state.
-      WDT_MR_WDIDLEHLT;                   // WDT stops in idle state.
-
-    #if ENABLED(WATCHDOG_RESET_MANUAL)
-      // We enable the watchdog timer, but only for the interrupt.
-
-      // Configure WDT to only trigger an interrupt
-      value |= WDT_MR_WDFIEN;             // Enable WDT fault interrupt.
-
-      // Disable WDT interrupt (just in case, to avoid triggering it!)
-      NVIC_DisableIRQ(WDT_IRQn);
-
-      // We NEED memory barriers to ensure Interrupts are actually disabled!
-      // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
-      __DSB();
-      __ISB();
-
-      // Initialize WDT with the given parameters
-      WDT_Enable(WDT, value);
-
-      // Configure and enable WDT interrupt.
-      NVIC_ClearPendingIRQ(WDT_IRQn);
-      NVIC_SetPriority(WDT_IRQn, 0); // Use highest priority, so we detect all kinds of lockups
-      NVIC_EnableIRQ(WDT_IRQn);
-
-    #else
-
-      // a WDT fault triggers a reset
-      value |= WDT_MR_WDRSTEN;
-
-      // Initialize WDT with the given parameters
-      WDT_Enable(WDT, value);
-
-    #endif
-
-    // Reset the watchdog
-    WDT_Restart(WDT);
-
-  #else
-
-    // Make sure to completely disable the Watchdog
-    WDT_Disable(WDT);
-
-  #endif
-}
-
-#if ENABLED(USE_WATCHDOG)
-  // Initialize watchdog - On SAM3X, Watchdog was already configured
-  //  and enabled or disabled at startup, so no need to reconfigure it
-  //  here.
-  void watchdog_init() {
-    // Reset watchdog to start clean
-    WDT_Restart(WDT);
-  }
-#endif // USE_WATCHDOG
-
-#endif
diff --git a/Marlin/src/HAL/ESP32/FlushableHardwareSerial.cpp b/Marlin/src/HAL/ESP32/FlushableHardwareSerial.cpp
index d4b2f42c53..145662215a 100644
--- a/Marlin/src/HAL/ESP32/FlushableHardwareSerial.cpp
+++ b/Marlin/src/HAL/ESP32/FlushableHardwareSerial.cpp
@@ -20,14 +20,10 @@
  *
  */
 
-#include "FlushableHardwareSerial.h"
-
 #ifdef ARDUINO_ARCH_ESP32
 
-FlushableHardwareSerial::FlushableHardwareSerial(int uart_nr)
-    : HardwareSerial(uart_nr)
-{}
+#include "FlushableHardwareSerial.h"
 
-FlushableHardwareSerial flushableSerial(0);
+Serial1Class flushableSerial(false, 0);
 
-#endif // ARDUINO_ARCH_ESP32
+#endif
diff --git a/Marlin/src/HAL/ESP32/FlushableHardwareSerial.h b/Marlin/src/HAL/ESP32/FlushableHardwareSerial.h
index b43caea13c..012dda8626 100644
--- a/Marlin/src/HAL/ESP32/FlushableHardwareSerial.h
+++ b/Marlin/src/HAL/ESP32/FlushableHardwareSerial.h
@@ -21,17 +21,14 @@
  */
 #pragma once
 
-#ifdef ARDUINO_ARCH_ESP32
-
 #include 
 
+#include "../shared/Marduino.h"
+#include "../../core/serial_hook.h"
+
 class FlushableHardwareSerial : public HardwareSerial {
 public:
-  FlushableHardwareSerial(int uart_nr);
-
-  inline void flushTX() { /* No need to flush the hardware serial, but defined here for compatibility. */ }
+  FlushableHardwareSerial(int uart_nr) : HardwareSerial(uart_nr) {}
 };
 
-extern FlushableHardwareSerial flushableSerial;
-
-#endif // ARDUINO_ARCH_ESP32
+extern Serial1Class flushableSerial;
diff --git a/Marlin/src/HAL/ESP32/HAL.cpp b/Marlin/src/HAL/ESP32/HAL.cpp
index 1e00df5177..29f3be3c02 100644
--- a/Marlin/src/HAL/ESP32/HAL.cpp
+++ b/Marlin/src/HAL/ESP32/HAL.cpp
@@ -28,6 +28,10 @@
 #include 
 #include 
 
+#if ENABLED(USE_ESP32_TASK_WDT)
+  #include 
+#endif
+
 #if ENABLED(WIFISUPPORT)
   #include 
   #include "wifi.h"
@@ -40,11 +44,15 @@
   #endif
 #endif
 
+#if ENABLED(ESP3D_WIFISUPPORT)
+  DefaultSerial1 MSerial0(false, Serial2Socket);
+#endif
+
 // ------------------------
 // Externs
 // ------------------------
 
-portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
+portMUX_TYPE MarlinHAL::spinlock = portMUX_INITIALIZER_UNLOCKED;
 
 // ------------------------
 // Local defines
@@ -56,7 +64,8 @@ portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
 // Public Variables
 // ------------------------
 
-uint16_t HAL_adc_result;
+uint16_t MarlinHAL::adc_result;
+pwm_pin_t MarlinHAL::pwm_pin_data[MAX_EXPANDER_BITS];
 
 // ------------------------
 // Private Variables
@@ -65,9 +74,16 @@ uint16_t HAL_adc_result;
 esp_adc_cal_characteristics_t characteristics[ADC_ATTEN_MAX];
 adc_atten_t attenuations[ADC1_CHANNEL_MAX] = {};
 uint32_t thresholds[ADC_ATTEN_MAX];
-volatile int numPWMUsed = 0,
-             pwmPins[MAX_PWM_PINS],
-             pwmValues[MAX_PWM_PINS];
+
+volatile int numPWMUsed = 0;
+volatile struct { pin_t pin; int value; } pwmState[MAX_PWM_PINS];
+
+pin_t chan_pin[CHANNEL_MAX_NUM + 1] = { 0 }; // PWM capable IOpins - not 0 or >33 on ESP32
+
+struct {
+  uint32_t freq; // ledcReadFreq doesn't work if a duty hasn't been set yet!
+  uint16_t res;
+} pwmInfo[(CHANNEL_MAX_NUM + 1) / 2];
 
 // ------------------------
 // Public functions
@@ -86,10 +102,26 @@ volatile int numPWMUsed = 0,
 
 #endif
 
-void HAL_init() { i2s_init(); }
+#if ENABLED(USE_ESP32_EXIO)
 
-void HAL_init_board() {
+  HardwareSerial YSerial2(2);
 
+  void Write_EXIO(uint8_t IO, uint8_t v) {
+    if (hal.isr_state()) {
+      hal.isr_off();
+      YSerial2.write(0x80 | (((char)v) << 5) | (IO - 100));
+      hal.isr_on();
+    }
+    else
+      YSerial2.write(0x80 | (((char)v) << 5) | (IO - 100));
+  }
+
+#endif
+
+void MarlinHAL::init_board() {
+  #if ENABLED(USE_ESP32_TASK_WDT)
+    esp_task_wdt_init(10, true);
+  #endif
   #if ENABLED(ESP3D_WIFISUPPORT)
     esp3dlib.init();
   #elif ENABLED(WIFISUPPORT)
@@ -122,27 +154,61 @@ void HAL_init_board() {
     #endif
   #endif
 
+  // Initialize the i2s peripheral only if the I2S stepper stream is enabled.
+  // The following initialization is performed after Serial1 and Serial2 are defined as
+  // their native pins might conflict with the i2s stream even when they are remapped.
+  #if ENABLED(USE_ESP32_EXIO)
+    YSerial2.begin(460800 * 3, SERIAL_8N1, 16, 17);
+  #elif ENABLED(I2S_STEPPER_STREAM)
+    i2s_init();
+  #endif
 }
 
-void HAL_idletask() {
+void MarlinHAL::idletask() {
   #if BOTH(WIFISUPPORT, OTASUPPORT)
     OTA_handle();
   #endif
   TERN_(ESP3D_WIFISUPPORT, esp3dlib.idletask());
 }
 
-void HAL_clear_reset_source() { }
+uint8_t MarlinHAL::get_reset_source() { return rtc_get_reset_reason(1); }
 
-uint8_t HAL_get_reset_source() { return rtc_get_reset_reason(1); }
+void MarlinHAL::reboot() { ESP.restart(); }
 
 void _delay_ms(int delay_ms) { delay(delay_ms); }
 
 // return free memory between end of heap (or end bss) and whatever is current
-int freeMemory() { return ESP.getFreeHeap(); }
+int MarlinHAL::freeMemory() { return ESP.getFreeHeap(); }
+
+// ------------------------
+// Watchdog Timer
+// ------------------------
+
+#if ENABLED(USE_WATCHDOG)
+
+  #define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout
+
+  extern "C" {
+    esp_err_t esp_task_wdt_reset();
+  }
+
+  void watchdogSetup() {
+    // do whatever. don't remove this function.
+  }
+
+  void MarlinHAL::watchdog_init() {
+    // TODO
+  }
+
+  // Reset watchdog.
+  void MarlinHAL::watchdog_refresh() { esp_task_wdt_reset(); }
+
+#endif
 
 // ------------------------
 // ADC
 // ------------------------
+
 #define ADC1_CHANNEL(pin) ADC1_GPIO ## pin ## _CHANNEL
 
 adc1_channel_t get_channel(int pin) {
@@ -164,21 +230,24 @@ void adc1_set_attenuation(adc1_channel_t chan, adc_atten_t atten) {
   }
 }
 
-void HAL_adc_init() {
+void MarlinHAL::adc_init() {
   // Configure ADC
   adc1_config_width(ADC_WIDTH_12Bit);
 
   // Configure channels only if used as (re-)configuring a pin for ADC that is used elsewhere might have adverse effects
-  TERN_(HAS_TEMP_ADC_0, adc1_set_attenuation(get_channel(TEMP_0_PIN), ADC_ATTEN_11db));
-  TERN_(HAS_TEMP_ADC_1, adc1_set_attenuation(get_channel(TEMP_1_PIN), ADC_ATTEN_11db));
-  TERN_(HAS_TEMP_ADC_2, adc1_set_attenuation(get_channel(TEMP_2_PIN), ADC_ATTEN_11db));
-  TERN_(HAS_TEMP_ADC_3, adc1_set_attenuation(get_channel(TEMP_3_PIN), ADC_ATTEN_11db));
-  TERN_(HAS_TEMP_ADC_4, adc1_set_attenuation(get_channel(TEMP_4_PIN), ADC_ATTEN_11db));
-  TERN_(HAS_TEMP_ADC_5, adc1_set_attenuation(get_channel(TEMP_5_PIN), ADC_ATTEN_11db));
-  TERN_(HAS_TEMP_ADC_6, adc2_set_attenuation(get_channel(TEMP_6_PIN), ADC_ATTEN_11db));
-  TERN_(HAS_TEMP_ADC_7, adc3_set_attenuation(get_channel(TEMP_7_PIN), ADC_ATTEN_11db));
-  TERN_(HAS_HEATED_BED, adc1_set_attenuation(get_channel(TEMP_BED_PIN), ADC_ATTEN_11db));
-  TERN_(HAS_TEMP_CHAMBER, adc1_set_attenuation(get_channel(TEMP_CHAMBER_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_ADC_0,        adc1_set_attenuation(get_channel(TEMP_0_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_ADC_1,        adc1_set_attenuation(get_channel(TEMP_1_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_ADC_2,        adc1_set_attenuation(get_channel(TEMP_2_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_ADC_3,        adc1_set_attenuation(get_channel(TEMP_3_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_ADC_4,        adc1_set_attenuation(get_channel(TEMP_4_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_ADC_5,        adc1_set_attenuation(get_channel(TEMP_5_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_ADC_6,        adc2_set_attenuation(get_channel(TEMP_6_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_ADC_7,        adc3_set_attenuation(get_channel(TEMP_7_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_HEATED_BED,        adc1_set_attenuation(get_channel(TEMP_BED_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_CHAMBER,      adc1_set_attenuation(get_channel(TEMP_CHAMBER_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_PROBE,        adc1_set_attenuation(get_channel(TEMP_PROBE_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_COOLER,       adc1_set_attenuation(get_channel(TEMP_COOLER_PIN), ADC_ATTEN_11db));
+  TERN_(HAS_TEMP_BOARD,        adc1_set_attenuation(get_channel(TEMP_BOARD_PIN), ADC_ATTEN_11db));
   TERN_(FILAMENT_WIDTH_SENSOR, adc1_set_attenuation(get_channel(FILWIDTH_PIN), ADC_ATTEN_11db));
 
   // Note that adc2 is shared with the WiFi module, which has higher priority, so the conversion may fail.
@@ -193,11 +262,16 @@ void HAL_adc_init() {
   }
 }
 
-void HAL_adc_start_conversion(const uint8_t adc_pin) {
-  const adc1_channel_t chan = get_channel(adc_pin);
+#ifndef ADC_REFERENCE_VOLTAGE
+  #define ADC_REFERENCE_VOLTAGE 3.3
+#endif
+
+void MarlinHAL::adc_start(const pin_t pin) {
+  const adc1_channel_t chan = get_channel(pin);
   uint32_t mv;
   esp_adc_cal_get_voltage((adc_channel_t)chan, &characteristics[attenuations[chan]], &mv);
-  HAL_adc_result = mv * 1023.0 / 3300.0;
+
+  adc_result = mv * isr_float_t(1023) / isr_float_t(ADC_REFERENCE_VOLTAGE) / isr_float_t(1000);
 
   // Change the attenuation level based on the new reading
   adc_atten_t atten;
@@ -214,25 +288,106 @@ void HAL_adc_start_conversion(const uint8_t adc_pin) {
   adc1_set_attenuation(chan, atten);
 }
 
-void analogWrite(pin_t pin, int value) {
-  // Use ledc hardware for internal pins
-  if (pin < 34) {
-    static int cnt_channel = 1, pin_to_channel[40] = { 0 };
-    if (pin_to_channel[pin] == 0) {
-      ledcAttachPin(pin, cnt_channel);
-      ledcSetup(cnt_channel, 490, 8);
-      ledcWrite(cnt_channel, value);
-      pin_to_channel[pin] = cnt_channel++;
+// ------------------------
+// PWM
+// ------------------------
+
+int8_t channel_for_pin(const uint8_t pin) {
+  for (int i = 0; i <= CHANNEL_MAX_NUM; i++)
+    if (chan_pin[i] == pin) return i;
+  return -1;
+}
+
+// get PWM channel for pin - if none then attach a new one
+// return -1 if fail or invalid pin#, channel # (0-15) if success
+int8_t get_pwm_channel(const pin_t pin, const uint32_t freq, const uint16_t res) {
+  if (!WITHIN(pin, 1, MAX_PWM_IOPIN)) return -1; // Not a hardware PWM pin!
+  int8_t cid = channel_for_pin(pin);
+  if (cid >= 0) return cid;
+
+  // Find an empty adjacent channel (same timer & freq/res)
+  for (int i = 0; i <= CHANNEL_MAX_NUM; i++) {
+    if (chan_pin[i] == 0) {
+      if (chan_pin[i ^ 0x1] != 0) {
+        if (pwmInfo[i / 2].freq == freq && pwmInfo[i / 2].res == res) {
+          chan_pin[i] = pin; // Allocate PWM to this channel
+          ledcAttachPin(pin, i);
+          return i;
+        }
+      }
+      else if (cid == -1)    // Pair of empty channels?
+        cid = i & 0xFE;      // Save lower channel number
     }
-    ledcWrite(pin_to_channel[pin], value);
+  }
+  // not attached, is an empty timer slot avail?
+  if (cid >= 0) {
+    chan_pin[cid] = pin;
+    pwmInfo[cid / 2].freq = freq;
+    pwmInfo[cid / 2].res = res;
+    ledcSetup(cid, freq, res);
+    ledcAttachPin(pin, cid);
+  }
+  return cid; // -1 if no channel avail
+}
+
+void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=_BV(PWM_RESOLUTION)-1*/, const bool invert/*=false*/) {
+  #if ENABLED(I2S_STEPPER_STREAM)
+    if (pin > 127) {
+      const uint8_t pinlo = pin & 0x7F;
+      pwm_pin_t &pindata = pwm_pin_data[pinlo];
+      const uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, pindata.pwm_cycle_ticks);
+      if (duty == 0 || duty == pindata.pwm_cycle_ticks) { // max or min (i.e., on/off)
+        pindata.pwm_duty_ticks = 0;  // turn off PWM for this pin
+        duty ? SBI32(i2s_port_data, pinlo) : CBI32(i2s_port_data, pinlo); // set pin level
+      }
+      else
+        pindata.pwm_duty_ticks = duty; // PWM duty count = # of 4µs ticks per full PWM cycle
+    }
+    else
+  #endif
+    {
+      const int8_t cid = get_pwm_channel(pin, PWM_FREQUENCY, PWM_RESOLUTION);
+      if (cid >= 0) {
+        const uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, _BV(PWM_RESOLUTION)-1);
+        ledcWrite(cid, duty);
+      }
+    }
+}
+
+int8_t MarlinHAL::set_pwm_frequency(const pin_t pin, const uint32_t f_desired) {
+  #if ENABLED(I2S_STEPPER_STREAM)
+    if (pin > 127) {
+      pwm_pin_data[pin & 0x7F].pwm_cycle_ticks = 1000000UL / f_desired / 4; // # of 4µs ticks per full PWM cycle
+      return 0;
+    }
+    else
+  #endif
+    {
+      const int8_t cid = channel_for_pin(pin);
+      if (cid >= 0) {
+        if (f_desired == ledcReadFreq(cid)) return cid; // no freq change
+        ledcDetachPin(chan_pin[cid]);
+        chan_pin[cid] = 0;              // remove old freq channel
+      }
+      return get_pwm_channel(pin, f_desired, PWM_RESOLUTION); // try for new one
+    }
+}
+
+// use hardware PWM if avail, if not then ISR
+void analogWrite(const pin_t pin, const uint16_t value, const uint32_t freq/*=PWM_FREQUENCY*/, const uint16_t res/*=8*/) { // always 8 bit resolution!
+  // Use ledc hardware for internal pins
+  const int8_t cid = get_pwm_channel(pin, freq, res);
+  if (cid >= 0) {
+    ledcWrite(cid, value); // set duty value
     return;
   }
 
+  // not a hardware PWM pin OR no PWM channels available
   int idx = -1;
 
   // Search Pin
   for (int i = 0; i < numPWMUsed; ++i)
-    if (pwmPins[i] == pin) { idx = i; break; }
+    if (pwmState[i].pin == pin) { idx = i; break; }
 
   // not found ?
   if (idx < 0) {
@@ -241,34 +396,34 @@ void analogWrite(pin_t pin, int value) {
 
     // Take new slot for pin
     idx = numPWMUsed;
-    pwmPins[idx] = pin;
+    pwmState[idx].pin = pin;
     // Start timer on first use
-    if (idx == 0) HAL_timer_start(PWM_TIMER_NUM, PWM_TIMER_FREQUENCY);
+    if (idx == 0) HAL_timer_start(MF_TIMER_PWM, PWM_TIMER_FREQUENCY);
 
     ++numPWMUsed;
   }
 
   // Use 7bit internal value - add 1 to have 100% high at 255
-  pwmValues[idx] = (value + 1) / 2;
+  pwmState[idx].value = (value + 1) / 2;
 }
 
 // Handle PWM timer interrupt
 HAL_PWM_TIMER_ISR() {
-  HAL_timer_isr_prologue(PWM_TIMER_NUM);
+  HAL_timer_isr_prologue(MF_TIMER_PWM);
 
   static uint8_t count = 0;
 
   for (int i = 0; i < numPWMUsed; ++i) {
     if (count == 0)                   // Start of interval
-      WRITE(pwmPins[i], pwmValues[i] ? HIGH : LOW);
-    else if (pwmValues[i] == count)   // End of duration
-      WRITE(pwmPins[i], LOW);
+      digitalWrite(pwmState[i].pin, pwmState[i].value ? HIGH : LOW);
+    else if (pwmState[i].value == count)   // End of duration
+      digitalWrite(pwmState[i].pin, LOW);
   }
 
   // 128 for 7 Bit resolution
   count = (count + 1) & 0x7F;
 
-  HAL_timer_isr_epilogue(PWM_TIMER_NUM);
+  HAL_timer_isr_epilogue(MF_TIMER_PWM);
 }
 
 #endif // ARDUINO_ARCH_ESP32
diff --git a/Marlin/src/HAL/ESP32/HAL.h b/Marlin/src/HAL/ESP32/HAL.h
index ebc16c9525..ddfedf92ee 100644
--- a/Marlin/src/HAL/ESP32/HAL.h
+++ b/Marlin/src/HAL/ESP32/HAL.h
@@ -32,7 +32,6 @@
 #include "../shared/HAL_SPI.h"
 
 #include "fastio.h"
-#include "watchdog.h"
 #include "i2s.h"
 
 #if ENABLED(WIFISUPPORT)
@@ -49,87 +48,68 @@
 // Defines
 // ------------------------
 
-extern portMUX_TYPE spinlock;
-
-#define MYSERIAL0 flushableSerial
+#define MYSERIAL1 flushableSerial
 
 #if EITHER(WIFISUPPORT, ESP3D_WIFISUPPORT)
   #if ENABLED(ESP3D_WIFISUPPORT)
-    #define MYSERIAL1 Serial2Socket
+    typedef ForwardSerial1Class< decltype(Serial2Socket) > DefaultSerial1;
+    extern DefaultSerial1 MSerial0;
+    #define MYSERIAL2 MSerial0
   #else
-    #define MYSERIAL1 webSocketSerial
+    #define MYSERIAL2 webSocketSerial
   #endif
 #endif
 
-#define CRITICAL_SECTION_START() portENTER_CRITICAL(&spinlock)
-#define CRITICAL_SECTION_END()   portEXIT_CRITICAL(&spinlock)
-#define ISRS_ENABLED() (spinlock.owner == portMUX_FREE_VAL)
-#define ENABLE_ISRS()  if (spinlock.owner != portMUX_FREE_VAL) portEXIT_CRITICAL(&spinlock)
-#define DISABLE_ISRS() portENTER_CRITICAL(&spinlock)
+#define CRITICAL_SECTION_START() portENTER_CRITICAL(&hal.spinlock)
+#define CRITICAL_SECTION_END()   portEXIT_CRITICAL(&hal.spinlock)
 
-// Fix bug in pgm_read_ptr
-#undef pgm_read_ptr
-#define pgm_read_ptr(addr) (*(addr))
+#define HAL_CAN_SET_PWM_FREQ   // This HAL supports PWM Frequency adjustment
+#define PWM_FREQUENCY  1000u   // Default PWM frequency when set_pwm_duty() is called without set_pwm_frequency()
+#define PWM_RESOLUTION   10u   // Default PWM bit resolution
+#define CHANNEL_MAX_NUM  15u   // max PWM channel # to allocate (7 to only use low speed, 15 to use low & high)
+#define MAX_PWM_IOPIN    33u   // hardware pwm pins < 34
+#ifndef MAX_EXPANDER_BITS
+  #define MAX_EXPANDER_BITS 32 // I2S expander bit width (max 32)
+#endif
 
 // ------------------------
 // Types
 // ------------------------
 
+typedef double isr_float_t;   // FPU ops are used for single-precision, so use double for ISRs.
 typedef int16_t pin_t;
 
-#define HAL_SERVO_LIB Servo
+typedef struct pwm_pin {
+  uint32_t pwm_cycle_ticks = 1000000UL / (PWM_FREQUENCY) / 4; // # ticks per pwm cycle
+  uint32_t pwm_tick_count = 0;  // current tick count
+  uint32_t pwm_duty_ticks = 0;  // # of ticks for current duty cycle
+} pwm_pin_t;
 
-// ------------------------
-// Public Variables
-// ------------------------
-
-/** result of last ADC conversion */
-extern uint16_t HAL_adc_result;
+class Servo;
+typedef Servo hal_servo_t;
 
 // ------------------------
 // Public functions
 // ------------------------
 
-// clear reset reason
-void HAL_clear_reset_source();
-
-// reset reason
-uint8_t HAL_get_reset_source();
-
-inline void HAL_reboot() {}  // reboot the board or restart the bootloader
-
-void _delay_ms(int delay);
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-int freeMemory();
-#pragma GCC diagnostic pop
-
-void analogWrite(pin_t pin, int value);
-
-// ADC
-#define HAL_ANALOG_SELECT(pin)
-
-void HAL_adc_init();
-
-#define HAL_ADC_VREF         3.3
-#define HAL_ADC_RESOLUTION  10
-#define HAL_START_ADC(pin)  HAL_adc_start_conversion(pin)
-#define HAL_READ_ADC()      HAL_adc_result
-#define HAL_ADC_READY()     true
-
-void HAL_adc_start_conversion(const uint8_t adc_pin);
+//
+// Tone
+//
+void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration=0);
+void noTone(const pin_t _pin);
+int8_t get_pwm_channel(const pin_t pin, const uint32_t freq, const uint16_t res);
+void analogWrite(const pin_t pin, const uint16_t value, const uint32_t freq=PWM_FREQUENCY, const uint16_t res=8);
 
+//
+// Pin Mapping for M42, M43, M226
+//
 #define GET_PIN_MAP_PIN(index) index
 #define GET_PIN_MAP_INDEX(pin) pin
 #define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval)
 
-// Enable hooks into idle and setup for HAL
-#define HAL_IDLETASK 1
-#define BOARD_INIT() HAL_init_board();
-void HAL_idletask();
-void HAL_init();
-void HAL_init_board();
+#if ENABLED(USE_ESP32_EXIO)
+  void Write_EXIO(uint8_t IO, uint8_t v);
+#endif
 
 //
 // Delay in cycles (used by DELAY_NS / DELAY_US)
@@ -171,3 +151,96 @@ FORCE_INLINE static void DELAY_CYCLES(uint32_t x) {
   }
 
 }
+
+// ------------------------
+// Class Utilities
+// ------------------------
+
+#pragma GCC diagnostic push
+#if GCC_VERSION <= 50000
+  #pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+int freeMemory();
+
+#pragma GCC diagnostic pop
+
+void _delay_ms(const int ms);
+
+// ------------------------
+// MarlinHAL Class
+// ------------------------
+
+#define HAL_ADC_VREF         3.3
+#define HAL_ADC_RESOLUTION  10
+
+class MarlinHAL {
+public:
+
+  // Earliest possible init, before setup()
+  MarlinHAL() {}
+
+  // Watchdog
+  static void watchdog_init()    IF_DISABLED(USE_WATCHDOG, {});
+  static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {});
+
+  static void init() {}        // Called early in setup()
+  static void init_board();    // Called less early in setup()
+  static void reboot();        // Restart the firmware
+
+  // Interrupts
+  static portMUX_TYPE spinlock;
+  static bool isr_state() { return spinlock.owner == portMUX_FREE_VAL; }
+  static void isr_on()  { if (spinlock.owner != portMUX_FREE_VAL) portEXIT_CRITICAL(&spinlock); }
+  static void isr_off() { portENTER_CRITICAL(&spinlock); }
+
+  static void delay_ms(const int ms) { _delay_ms(ms); }
+
+  // Tasks, called from idle()
+  static void idletask();
+
+  // Reset
+  static uint8_t get_reset_source();
+  static void clear_reset_source() {}
+
+  // Free SRAM
+  static int freeMemory();
+
+  static pwm_pin_t pwm_pin_data[MAX_EXPANDER_BITS];
+
+  //
+  // ADC Methods
+  //
+
+  static uint16_t adc_result;
+
+  // Called by Temperature::init once at startup
+  static void adc_init();
+
+  // Called by Temperature::init for each sensor at startup
+  static void adc_enable(const pin_t pin) {}
+
+  // Begin ADC sampling on the given pin. Called from Temperature::isr!
+  static void adc_start(const pin_t pin);
+
+  // Is the ADC ready for reading?
+  static bool adc_ready() { return true; }
+
+  // The current value of the ADC register
+  static uint16_t adc_value() { return adc_result; }
+
+  /**
+   * If not already allocated, allocate a hardware PWM channel
+   * to the pin and set the duty cycle..
+   * Optionally invert the duty cycle [default = false]
+   * Optionally change the scale of the provided value to enable finer PWM duty control [default = 255]
+   */
+  static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);
+
+  /**
+   * Allocate and set the frequency of a hardware PWM pin
+   * Returns -1 if no pin available.
+   */
+  static int8_t set_pwm_frequency(const pin_t pin, const uint32_t f_desired);
+
+};
diff --git a/Marlin/src/HAL/ESP32/HAL_SPI.cpp b/Marlin/src/HAL/ESP32/HAL_SPI.cpp
index 8e5875fc38..868ab1b671 100644
--- a/Marlin/src/HAL/ESP32/HAL_SPI.cpp
+++ b/Marlin/src/HAL/ESP32/HAL_SPI.cpp
@@ -53,11 +53,9 @@ static SPISettings spiConfig;
 // ------------------------
 
 void spiBegin() {
-  #if !PIN_EXISTS(SS)
-    #error "SS_PIN not defined!"
+  #if ENABLED(SDSUPPORT) && PIN_EXISTS(SD_SS)
+    OUT_WRITE(SD_SS_PIN, HIGH);
   #endif
-
-  OUT_WRITE(SS_PIN, HIGH);
 }
 
 void spiInit(uint8_t spiRate) {
@@ -85,7 +83,7 @@ uint8_t spiRec() {
   return returnByte;
 }
 
-void spiRead(uint8_t* buf, uint16_t nbyte) {
+void spiRead(uint8_t *buf, uint16_t nbyte) {
   SPI.beginTransaction(spiConfig);
   SPI.transferBytes(0, buf, nbyte);
   SPI.endTransaction();
@@ -97,7 +95,7 @@ void spiSend(uint8_t b) {
   SPI.endTransaction();
 }
 
-void spiSendBlock(uint8_t token, const uint8_t* buf) {
+void spiSendBlock(uint8_t token, const uint8_t *buf) {
   SPI.beginTransaction(spiConfig);
   SPI.transfer(token);
   SPI.writeBytes(const_cast(buf), 512);
diff --git a/Marlin/src/HAL/ESP32/MarlinSPI.h b/Marlin/src/HAL/ESP32/MarlinSPI.h
new file mode 100644
index 0000000000..0c447ba4cb
--- /dev/null
+++ b/Marlin/src/HAL/ESP32/MarlinSPI.h
@@ -0,0 +1,26 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ *
+ */
+#pragma once
+
+#include 
+
+using MarlinSPI = SPIClass;
diff --git a/Marlin/src/HAL/ESP32/Servo.cpp b/Marlin/src/HAL/ESP32/Servo.cpp
index fcf5848581..ca3950d07f 100644
--- a/Marlin/src/HAL/ESP32/Servo.cpp
+++ b/Marlin/src/HAL/ESP32/Servo.cpp
@@ -31,20 +31,18 @@
 // so we only allocate servo channels up high to avoid side effects with regards to analogWrite (fans, leds, laser pwm etc.)
 int Servo::channel_next_free = 12;
 
-Servo::Servo() {
-  channel = channel_next_free++;
-}
+Servo::Servo() {}
 
 int8_t Servo::attach(const int inPin) {
-  if (channel >= CHANNEL_MAX_NUM) return -1;
   if (inPin > 0) pin = inPin;
-
-  ledcSetup(channel, 50, 16); // channel X, 50 Hz, 16-bit depth
-  ledcAttachPin(pin, channel);
-  return true;
+  channel = get_pwm_channel(pin, 50u, 16u);
+  return channel; // -1 if no PWM avail.
 }
 
-void Servo::detach() { ledcDetachPin(pin); }
+// leave channel connected to servo - set duty to zero
+void Servo::detach() {
+  if (channel >= 0) ledcWrite(channel, 0);
+}
 
 int Servo::read() { return degrees; }
 
@@ -52,7 +50,7 @@ void Servo::write(int inDegrees) {
   degrees = constrain(inDegrees, MIN_ANGLE, MAX_ANGLE);
   int us = map(degrees, MIN_ANGLE, MAX_ANGLE, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
   int duty = map(us, 0, TAU_USEC, 0, MAX_COMPARE);
-  ledcWrite(channel, duty);
+  if (channel >= 0) ledcWrite(channel, duty); // don't save duty for servos!
 }
 
 void Servo::move(const int value) {
diff --git a/Marlin/src/HAL/ESP32/Servo.h b/Marlin/src/HAL/ESP32/Servo.h
index b0d9294527..1dbb416a83 100644
--- a/Marlin/src/HAL/ESP32/Servo.h
+++ b/Marlin/src/HAL/ESP32/Servo.h
@@ -30,8 +30,7 @@ class Servo {
                    MAX_PULSE_WIDTH = 2400,  // Longest pulse sent to a servo
                    TAU_MSEC = 20,
                    TAU_USEC = (TAU_MSEC * 1000),
-                   MAX_COMPARE = ((1 << 16) - 1), // 65535
-                   CHANNEL_MAX_NUM = 16;
+                   MAX_COMPARE = _BV(16) - 1; // 65535
 
 public:
   Servo();
diff --git a/Marlin/src/HAL/STM32F1/watchdog.cpp b/Marlin/src/HAL/ESP32/Tone.cpp
similarity index 52%
rename from Marlin/src/HAL/STM32F1/watchdog.cpp
rename to Marlin/src/HAL/ESP32/Tone.cpp
index ca91a6fe43..839c612b6a 100644
--- a/Marlin/src/HAL/STM32F1/watchdog.cpp
+++ b/Marlin/src/HAL/ESP32/Tone.cpp
@@ -5,6 +5,8 @@
  * Based on Sprinter and grbl.
  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  *
+ * Copypaste of SAMD51 HAL developed by Giuliano Zaro (AKA GMagician)
+ *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
@@ -21,41 +23,37 @@
  */
 
 /**
- * HAL for stm32duino.com based on Libmaple and compatible (STM32F1)
+ * Description: Tone function for ESP32
+ * Derived from https://forum.arduino.cc/index.php?topic=136500.msg2903012#msg2903012
  */
 
-#ifdef __STM32F1__
+#ifdef ARDUINO_ARCH_ESP32
 
 #include "../../inc/MarlinConfig.h"
+#include "HAL.h"
 
-#if ENABLED(USE_WATCHDOG)
+static pin_t tone_pin;
+volatile static int32_t toggles;
 
-#include 
-#include "watchdog.h"
-
-void HAL_watchdog_refresh() {
-  #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED)
-    TOGGLE(LED_PIN);  // heartbeat indicator
-  #endif
-  iwdg_feed();
+void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration/*=0*/) {
+  tone_pin = _pin;
+  toggles = 2 * frequency * duration / 1000;
+  HAL_timer_start(MF_TIMER_TONE, 2 * frequency);
 }
 
-void watchdogSetup() {
-  // do whatever. don't remove this function.
+void noTone(const pin_t _pin) {
+  HAL_timer_disable_interrupt(MF_TIMER_TONE);
+  WRITE(_pin, LOW);
 }
 
-/**
- * @brief  Initialized the independent hardware watchdog.
- *
- * @return No return
- *
- * @details The watchdog clock is 40Khz. We need a 4 seconds interval, so use a /256 preescaler and 625 reload value (counts down to 0)
- */
-void watchdog_init() {
-  #if DISABLED(DISABLE_WATCHDOG_INIT)
-    iwdg_init(IWDG_PRE_256, STM32F1_WD_RELOAD);
-  #endif
+HAL_TONE_TIMER_ISR() {
+  HAL_timer_isr_prologue(MF_TIMER_TONE);
+
+  if (toggles) {
+    toggles--;
+    TOGGLE(tone_pin);
+  }
+  else noTone(tone_pin);                         // turn off interrupt
 }
 
-#endif // USE_WATCHDOG
-#endif // __STM32F1__
+#endif // ARDUINO_ARCH_ESP32
diff --git a/Marlin/src/HAL/ESP32/WebSocketSerial.cpp b/Marlin/src/HAL/ESP32/WebSocketSerial.cpp
index ca7f47a1f8..eb5b9d6039 100644
--- a/Marlin/src/HAL/ESP32/WebSocketSerial.cpp
+++ b/Marlin/src/HAL/ESP32/WebSocketSerial.cpp
@@ -29,7 +29,7 @@
 #include "wifi.h"
 #include 
 
-WebSocketSerial webSocketSerial;
+MSerialWebSocketT webSocketSerial(false);
 AsyncWebSocket ws("/ws"); // TODO Move inside the class.
 
 // RingBuffer impl
@@ -137,16 +137,12 @@ size_t WebSocketSerial::write(const uint8_t c) {
   return ret;
 }
 
-size_t WebSocketSerial::write(const uint8_t* buffer, size_t size) {
+size_t WebSocketSerial::write(const uint8_t *buffer, size_t size) {
   size_t written = 0;
   for (size_t i = 0; i < size; i++)
     written += write(buffer[i]);
   return written;
 }
 
-void WebSocketSerial::flushTX() {
-  // No need to do anything as there's no benefit to sending partial lines over the websocket connection.
-}
-
 #endif // WIFISUPPORT
 #endif // ARDUINO_ARCH_ESP32
diff --git a/Marlin/src/HAL/ESP32/WebSocketSerial.h b/Marlin/src/HAL/ESP32/WebSocketSerial.h
index 7a25c6dc5e..6b3e419d10 100644
--- a/Marlin/src/HAL/ESP32/WebSocketSerial.h
+++ b/Marlin/src/HAL/ESP32/WebSocketSerial.h
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "../../inc/MarlinConfig.h"
+#include "../../core/serial_hook.h"
 
 #include 
 
@@ -53,7 +54,7 @@ public:
   ring_buffer_pos_t read(uint8_t *buffer);
   void flush();
   ring_buffer_pos_t write(const uint8_t c);
-  ring_buffer_pos_t write(const uint8_t* buffer, ring_buffer_pos_t size);
+  ring_buffer_pos_t write(const uint8_t *buffer, ring_buffer_pos_t size);
 };
 
 class WebSocketSerial: public Stream {
@@ -68,11 +69,8 @@ public:
   int peek();
   int read();
   void flush();
-  void flushTX();
   size_t write(const uint8_t c);
-  size_t write(const uint8_t* buffer, size_t size);
-
-  operator bool() { return true; }
+  size_t write(const uint8_t *buffer, size_t size);
 
   #if ENABLED(SERIAL_STATS_DROPPED_RX)
     FORCE_INLINE uint32_t dropped() { return 0; }
@@ -83,4 +81,5 @@ public:
   #endif
 };
 
-extern WebSocketSerial webSocketSerial;
+typedef Serial1Class MSerialWebSocketT;
+extern MSerialWebSocketT webSocketSerial;
diff --git a/Marlin/src/HAL/ESP32/eeprom.cpp b/Marlin/src/HAL/ESP32/eeprom.cpp
index 1bf687c6fe..cb5f881284 100644
--- a/Marlin/src/HAL/ESP32/eeprom.cpp
+++ b/Marlin/src/HAL/ESP32/eeprom.cpp
@@ -44,7 +44,7 @@ bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, ui
   return false;
 }
 
-bool PersistentStore::read_data(int &pos, uint8_t* value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
+bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
   for (size_t i = 0; i < size; i++) {
     uint8_t c = EEPROM.read(pos++);
     if (writing) value[i] = c;
diff --git a/Marlin/src/HAL/ESP32/endstop_interrupts.h b/Marlin/src/HAL/ESP32/endstop_interrupts.h
index 743ccd99c9..0536864610 100644
--- a/Marlin/src/HAL/ESP32/endstop_interrupts.h
+++ b/Marlin/src/HAL/ESP32/endstop_interrupts.h
@@ -59,4 +59,16 @@ void setup_endstop_interrupts() {
   TERN_(HAS_Z4_MAX, _ATTACH(Z4_MAX_PIN));
   TERN_(HAS_Z4_MIN, _ATTACH(Z4_MIN_PIN));
   TERN_(HAS_Z_MIN_PROBE_PIN, _ATTACH(Z_MIN_PROBE_PIN));
+  TERN_(HAS_I_MAX, _ATTACH(I_MAX_PIN));
+  TERN_(HAS_I_MIN, _ATTACH(I_MIN_PIN));
+  TERN_(HAS_J_MAX, _ATTACH(J_MAX_PIN));
+  TERN_(HAS_J_MIN, _ATTACH(J_MIN_PIN));
+  TERN_(HAS_K_MAX, _ATTACH(K_MAX_PIN));
+  TERN_(HAS_K_MIN, _ATTACH(K_MIN_PIN));
+  TERN_(HAS_U_MAX, _ATTACH(U_MAX_PIN));
+  TERN_(HAS_U_MIN, _ATTACH(U_MIN_PIN));
+  TERN_(HAS_V_MAX, _ATTACH(V_MAX_PIN));
+  TERN_(HAS_V_MIN, _ATTACH(V_MIN_PIN));
+  TERN_(HAS_W_MAX, _ATTACH(W_MAX_PIN));
+  TERN_(HAS_W_MIN, _ATTACH(W_MIN_PIN));
 }
diff --git a/Marlin/src/HAL/ESP32/esp32.csv b/Marlin/src/HAL/ESP32/esp32.csv
new file mode 100644
index 0000000000..8f6e101f02
--- /dev/null
+++ b/Marlin/src/HAL/ESP32/esp32.csv
@@ -0,0 +1,6 @@
+# Name,   Type, SubType, Offset,  Size, Flags
+nvs,      data, nvs,     0x9000,   0x5000,
+otadata,  data, ota,     0xe000,   0x2000,
+app0,     app,  ota_0,   0x10000,  0x180000,
+app1,     app,  ota_1,   0x190000, 0x180000,
+spiffs,   data, spiffs,  0x310000, 0xF0000,
diff --git a/Marlin/src/HAL/ESP32/fastio.h b/Marlin/src/HAL/ESP32/fastio.h
index 2ded3a5f62..c8e3f7e343 100644
--- a/Marlin/src/HAL/ESP32/fastio.h
+++ b/Marlin/src/HAL/ESP32/fastio.h
@@ -40,18 +40,27 @@
 // Set pin as input with pullup mode
 #define _PULLUP(IO, v)          pinMode(IO, v ? INPUT_PULLUP : INPUT)
 
-// Read a pin wrapper
-#define READ(IO)                (IS_I2S_EXPANDER_PIN(IO) ? i2s_state(I2S_EXPANDER_PIN_INDEX(IO)) : digitalRead(IO))
+#if ENABLED(USE_ESP32_EXIO)
+  // Read a pin wrapper
+  #define READ(IO)                digitalRead(IO)
+  // Write to a pin wrapper
+  #define WRITE(IO, v)            (IO >= 100 ? Write_EXIO(IO, v) : digitalWrite(IO, v))
+#else
+  // Read a pin wrapper
+  #define READ(IO)                (IS_I2S_EXPANDER_PIN(IO) ? i2s_state(I2S_EXPANDER_PIN_INDEX(IO)) : digitalRead(IO))
+  // Write to a pin wrapper
+  #define WRITE(IO, v)            (IS_I2S_EXPANDER_PIN(IO) ? i2s_write(I2S_EXPANDER_PIN_INDEX(IO), v) : digitalWrite(IO, v))
+#endif
 
-// Write to a pin wrapper
-#define WRITE(IO, v)            (IS_I2S_EXPANDER_PIN(IO) ? i2s_write(I2S_EXPANDER_PIN_INDEX(IO), v) : digitalWrite(IO, v))
-
-// Set pin as input wrapper
+// Set pin as input wrapper (0x80 | (v << 5) | (IO - 100))
 #define SET_INPUT(IO)           _SET_INPUT(IO)
 
 // Set pin as input with pullup wrapper
 #define SET_INPUT_PULLUP(IO)    do{ _SET_INPUT(IO); _PULLUP(IO, HIGH); }while(0)
 
+// Set pin as input with pulldown (substitution)
+#define SET_INPUT_PULLDOWN      SET_INPUT
+
 // Set pin as output wrapper
 #define SET_OUTPUT(IO)          do{ _SET_OUTPUT(IO); }while(0)
 
diff --git a/Marlin/src/HAL/ESP32/i2s.cpp b/Marlin/src/HAL/ESP32/i2s.cpp
index 99b2f755e5..d9bad4ec2d 100644
--- a/Marlin/src/HAL/ESP32/i2s.cpp
+++ b/Marlin/src/HAL/ESP32/i2s.cpp
@@ -23,6 +23,8 @@
 
 #include "../../inc/MarlinConfigPre.h"
 
+#if DISABLED(USE_ESP32_EXIO)
+
 #include "i2s.h"
 
 #include "../shared/Marduino.h"
@@ -62,12 +64,9 @@ uint32_t i2s_port_data = 0;
 #define I2S_EXIT_CRITICAL()   portEXIT_CRITICAL(&i2s_spinlock[i2s_num])
 
 static inline void gpio_matrix_out_check(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) {
-  //if pin = -1, do not need to configure
-  if (gpio != -1) {
-    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
-    gpio_set_direction((gpio_num_t)gpio, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT);
-    gpio_matrix_out(gpio, signal_idx, out_inv, oen_inv);
-  }
+  PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
+  gpio_set_direction((gpio_num_t)gpio, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT);
+  gpio_matrix_out(gpio, signal_idx, out_inv, oen_inv);
 }
 
 static esp_err_t i2s_reset_fifo(i2s_port_t i2s_num) {
@@ -139,23 +138,41 @@ static void IRAM_ATTR i2s_intr_handler_default(void *arg) {
   I2S0.int_clr.val = I2S0.int_st.val; //clear pending interrupt
 }
 
-void stepperTask(void* parameter) {
-  uint32_t remaining = 0;
+void stepperTask(void *parameter) {
+  uint32_t nextMainISR = 0;
+  #if ENABLED(LIN_ADVANCE)
+    uint32_t nextAdvanceISR = Stepper::LA_ADV_NEVER;
+  #endif
 
-  while (1) {
+  for (;;) {
     xQueueReceive(dma.queue, &dma.current, portMAX_DELAY);
     dma.rw_pos = 0;
 
     while (dma.rw_pos < DMA_SAMPLE_COUNT) {
       // Fill with the port data post pulse_phase until the next step
-      if (remaining) {
+      if (nextMainISR && TERN1(LIN_ADVANCE, nextAdvanceISR))
         i2s_push_sample();
-        remaining--;
-      }
-      else {
+
+      // i2s_push_sample() is also called from Stepper::pulse_phase_isr() and Stepper::advance_isr()
+      // in a rare case where both are called, we need to double decrement the counters
+      const uint8_t push_count = 1 + (!nextMainISR && TERN0(LIN_ADVANCE, !nextAdvanceISR));
+
+      #if ENABLED(LIN_ADVANCE)
+        if (!nextAdvanceISR) {
+          Stepper::advance_isr();
+          nextAdvanceISR = Stepper::la_interval;
+        }
+        else if (nextAdvanceISR == Stepper::LA_ADV_NEVER)
+          nextAdvanceISR = Stepper::la_interval;
+      #endif
+
+      if (!nextMainISR) {
         Stepper::pulse_phase_isr();
-        remaining = Stepper::block_phase_isr();
+        nextMainISR = Stepper::block_phase_isr();
       }
+
+      nextMainISR -= push_count;
+      TERN_(LIN_ADVANCE, nextAdvanceISR -= push_count);
     }
   }
 }
@@ -184,7 +201,7 @@ int i2s_init() {
 
   // Allocate the array of pointers to the buffers
   dma.buffers = (uint32_t **)malloc(sizeof(uint32_t*) * DMA_BUF_COUNT);
-  if (dma.buffers == nullptr) return -1;
+  if (!dma.buffers) return -1;
 
   // Allocate each buffer that can be used by the DMA controller
   for (int buf_idx = 0; buf_idx < DMA_BUF_COUNT; buf_idx++) {
@@ -194,7 +211,7 @@ int i2s_init() {
 
   // Allocate the array of DMA descriptors
   dma.desc = (lldesc_t**) malloc(sizeof(lldesc_t*) * DMA_BUF_COUNT);
-  if (dma.desc == nullptr) return -1;
+  if (!dma.desc) return -1;
 
   // Allocate each DMA descriptor that will be used by the DMA controller
   for (int buf_idx = 0; buf_idx < DMA_BUF_COUNT; buf_idx++) {
@@ -254,13 +271,7 @@ int i2s_init() {
 
   I2S0.fifo_conf.dscr_en = 0;
 
-  I2S0.conf_chan.tx_chan_mod = (
-    #if ENABLED(I2S_STEPPER_SPLIT_STREAM)
-      4
-    #else
-      0
-    #endif
-  );
+  I2S0.conf_chan.tx_chan_mod = TERN(I2S_STEPPER_SPLIT_STREAM, 4, 0);
   I2S0.fifo_conf.tx_fifo_mod = 0;
   I2S0.conf.tx_mono = 0;
 
@@ -311,9 +322,16 @@ int i2s_init() {
   xTaskCreatePinnedToCore(stepperTask, "StepperTask", 10000, nullptr, 1, nullptr, CONFIG_ARDUINO_RUNNING_CORE); // run I2S stepper task on same core as rest of Marlin
 
   // Route the i2s pins to the appropriate GPIO
-  gpio_matrix_out_check(I2S_DATA, I2S0O_DATA_OUT23_IDX, 0, 0);
-  gpio_matrix_out_check(I2S_BCK, I2S0O_BCK_OUT_IDX, 0, 0);
-  gpio_matrix_out_check(I2S_WS, I2S0O_WS_OUT_IDX, 0, 0);
+  // If a pin is not defined, no need to configure
+  #if defined(I2S_DATA) && I2S_DATA >= 0
+    gpio_matrix_out_check(I2S_DATA, I2S0O_DATA_OUT23_IDX, 0, 0);
+  #endif
+  #if defined(I2S_BCK) && I2S_BCK >= 0
+    gpio_matrix_out_check(I2S_BCK, I2S0O_BCK_OUT_IDX, 0, 0);
+  #endif
+  #if defined(I2S_WS) && I2S_WS >= 0
+    gpio_matrix_out_check(I2S_WS, I2S0O_WS_OUT_IDX, 0, 0);
+  #endif
 
   // Start the I2S peripheral
   return i2s_start(I2S_NUM_0);
@@ -337,7 +355,28 @@ uint8_t i2s_state(uint8_t pin) {
 }
 
 void i2s_push_sample() {
+  // Every 4µs (when space in DMA buffer) toggle each expander PWM output using
+  // the current duty cycle/frequency so they sync with any steps (once
+  // through the DMA/FIFO buffers).  PWM signal inversion handled by other functions
+  LOOP_L_N(p, MAX_EXPANDER_BITS) {
+    if (hal.pwm_pin_data[p].pwm_duty_ticks > 0) { // pin has active pwm?
+      if (hal.pwm_pin_data[p].pwm_tick_count == 0) {
+        if (TEST32(i2s_port_data, p)) {  // hi->lo
+          CBI32(i2s_port_data, p);
+          hal.pwm_pin_data[p].pwm_tick_count = hal.pwm_pin_data[p].pwm_cycle_ticks - hal.pwm_pin_data[p].pwm_duty_ticks;
+        }
+        else { // lo->hi
+          SBI32(i2s_port_data, p);
+          hal.pwm_pin_data[p].pwm_tick_count = hal.pwm_pin_data[p].pwm_duty_ticks;
+        }
+      }
+      else
+        hal.pwm_pin_data[p].pwm_tick_count--;
+    }
+  }
+
   dma.current[dma.rw_pos++] = i2s_port_data;
 }
 
+#endif // !USE_ESP32_EXIO
 #endif // ARDUINO_ARCH_ESP32
diff --git a/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h b/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h
index 5f1c4b1601..3ca806897a 100644
--- a/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h
+++ b/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h
@@ -20,3 +20,10 @@
  *
  */
 #pragma once
+
+//
+// Board-specific options need to be defined before HAL.h
+//
+#if MB(MKS_TINYBEE)
+  #define MAX_EXPANDER_BITS 24  // TinyBee has 3 x HC595
+#endif
diff --git a/Marlin/src/HAL/ESP32/inc/SanityCheck.h b/Marlin/src/HAL/ESP32/inc/SanityCheck.h
index f57a6c5910..8c5621f10c 100644
--- a/Marlin/src/HAL/ESP32/inc/SanityCheck.h
+++ b/Marlin/src/HAL/ESP32/inc/SanityCheck.h
@@ -25,14 +25,34 @@
   #error "EMERGENCY_PARSER is not yet implemented for ESP32. Disable EMERGENCY_PARSER to continue."
 #endif
 
-#if ENABLED(FAST_PWM_FAN) || SPINDLE_LASER_FREQUENCY
-  #error "Features requiring Hardware PWM (FAST_PWM_FAN, SPINDLE_LASER_FREQUENCY) are not yet supported on ESP32."
+#if (ENABLED(SPINDLE_LASER_USE_PWM) && SPINDLE_LASER_FREQUENCY > 78125) || (ENABLED(FAST_PWM_FAN_FREQUENCY) && FAST_PWM_FAN_FREQUENCY > 78125)
+  #error "SPINDLE_LASER_FREQUENCY and FAST_PWM_FREQUENCY maximum value is 78125Hz for ESP32."
 #endif
 
 #if HAS_TMC_SW_SERIAL
-  #error "TMC220x Software Serial is not supported on this platform."
+  #error "TMC220x Software Serial is not supported on ESP32."
 #endif
 
 #if BOTH(WIFISUPPORT, ESP3D_WIFISUPPORT)
   #error "Only enable one WiFi option, either WIFISUPPORT or ESP3D_WIFISUPPORT."
 #endif
+
+#if ENABLED(POSTMORTEM_DEBUGGING)
+  #error "POSTMORTEM_DEBUGGING is not yet supported on ESP32."
+#endif
+
+#if MB(MKS_TINYBEE) && ENABLED(FAST_PWM_FAN)
+  #error "FAST_PWM_FAN is not available on TinyBee."
+#endif
+
+#if BOTH(I2S_STEPPER_STREAM, BABYSTEPPING) && DISABLED(INTEGRATED_BABYSTEPPING)
+  #error "BABYSTEPPING on I2S stream requires INTEGRATED_BABYSTEPPING."
+#endif
+
+#if USING_PULLDOWNS
+  #error "PULLDOWN pin mode is not available on ESP32 boards."
+#endif
+
+#if BOTH(I2S_STEPPER_STREAM, LIN_ADVANCE) && DISABLED(EXPERIMENTAL_I2S_LA)
+  #error "I2S stream is currently incompatible with LIN_ADVANCE."
+#endif
diff --git a/Marlin/src/HAL/ESP32/spi_pins.h b/Marlin/src/HAL/ESP32/spi_pins.h
index 15f8f2ab6b..58881f0ea7 100644
--- a/Marlin/src/HAL/ESP32/spi_pins.h
+++ b/Marlin/src/HAL/ESP32/spi_pins.h
@@ -2,6 +2,9 @@
  * Marlin 3D Printer Firmware
  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
@@ -18,7 +21,7 @@
  */
 #pragma once
 
-#define SS_PIN   SDSS
-#define SCK_PIN  18
-#define MISO_PIN 19
-#define MOSI_PIN 23
+#define SD_SS_PIN   SDSS
+#define SD_SCK_PIN  18
+#define SD_MISO_PIN 19
+#define SD_MOSI_PIN 23
diff --git a/Marlin/src/HAL/ESP32/timers.cpp b/Marlin/src/HAL/ESP32/timers.cpp
index 3300aea8a8..c37ad2430c 100644
--- a/Marlin/src/HAL/ESP32/timers.cpp
+++ b/Marlin/src/HAL/ESP32/timers.cpp
@@ -41,11 +41,11 @@
 
 static timg_dev_t *TG[2] = {&TIMERG0, &TIMERG1};
 
-const tTimerConfig TimerConfig [NUM_HARDWARE_TIMERS] = {
+const tTimerConfig timer_config[NUM_HARDWARE_TIMERS] = {
   { TIMER_GROUP_0, TIMER_0, STEPPER_TIMER_PRESCALE, stepTC_Handler }, // 0 - Stepper
   { TIMER_GROUP_0, TIMER_1,    TEMP_TIMER_PRESCALE, tempTC_Handler }, // 1 - Temperature
   { TIMER_GROUP_1, TIMER_0,     PWM_TIMER_PRESCALE, pwmTC_Handler  }, // 2 - PWM
-  { TIMER_GROUP_1, TIMER_1,                      1, nullptr }, // 3
+  { TIMER_GROUP_1, TIMER_1,    TONE_TIMER_PRESCALE, toneTC_Handler }, // 3 - Tone
 };
 
 // ------------------------
@@ -53,7 +53,7 @@ const tTimerConfig TimerConfig [NUM_HARDWARE_TIMERS] = {
 // ------------------------
 
 void IRAM_ATTR timer_isr(void *para) {
-  const tTimerConfig& timer = TimerConfig[(int)para];
+  const tTimerConfig& timer = timer_config[(int)para];
 
   // Retrieve the interrupt status and the counter value
   // from the timer that reported the interrupt
@@ -81,8 +81,8 @@ void IRAM_ATTR timer_isr(void *para) {
  * @param timer_num timer number to initialize
  * @param frequency frequency of the timer
  */
-void HAL_timer_start(const uint8_t timer_num, uint32_t frequency) {
-  const tTimerConfig timer = TimerConfig[timer_num];
+void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
+  const tTimerConfig timer = timer_config[timer_num];
 
   timer_config_t config;
   config.divider     = timer.divider;
@@ -115,7 +115,7 @@ void HAL_timer_start(const uint8_t timer_num, uint32_t frequency) {
  * @param count     threshold at which the interrupt is triggered
  */
 void HAL_timer_set_compare(const uint8_t timer_num, hal_timer_t count) {
-  const tTimerConfig timer = TimerConfig[timer_num];
+  const tTimerConfig timer = timer_config[timer_num];
   timer_set_alarm_value(timer.group, timer.idx, count);
 }
 
@@ -125,7 +125,7 @@ void HAL_timer_set_compare(const uint8_t timer_num, hal_timer_t count) {
  * @return           the timer current threshold for the alarm to be triggered
  */
 hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) {
-  const tTimerConfig timer = TimerConfig[timer_num];
+  const tTimerConfig timer = timer_config[timer_num];
 
   uint64_t alarm_value;
   timer_get_alarm_value(timer.group, timer.idx, &alarm_value);
@@ -139,7 +139,7 @@ hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) {
  * @return           the current counter of the alarm
  */
 hal_timer_t HAL_timer_get_count(const uint8_t timer_num) {
-  const tTimerConfig timer = TimerConfig[timer_num];
+  const tTimerConfig timer = timer_config[timer_num];
   uint64_t counter_value;
   timer_get_counter_value(timer.group, timer.idx, &counter_value);
   return counter_value;
@@ -150,7 +150,7 @@ hal_timer_t HAL_timer_get_count(const uint8_t timer_num) {
  * @param timer_num timer number to enable interrupts on
  */
 void HAL_timer_enable_interrupt(const uint8_t timer_num) {
-  //const tTimerConfig timer = TimerConfig[timer_num];
+  //const tTimerConfig timer = timer_config[timer_num];
   //timer_enable_intr(timer.group, timer.idx);
 }
 
@@ -159,12 +159,12 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num) {
  * @param timer_num timer number to disable interrupts on
  */
 void HAL_timer_disable_interrupt(const uint8_t timer_num) {
-  //const tTimerConfig timer = TimerConfig[timer_num];
+  //const tTimerConfig timer = timer_config[timer_num];
   //timer_disable_intr(timer.group, timer.idx);
 }
 
 bool HAL_timer_interrupt_enabled(const uint8_t timer_num) {
-  const tTimerConfig timer = TimerConfig[timer_num];
+  const tTimerConfig timer = timer_config[timer_num];
   return TG[timer.group]->int_ena.val | BIT(timer_num);
 }
 
diff --git a/Marlin/src/HAL/ESP32/timers.h b/Marlin/src/HAL/ESP32/timers.h
index d722670f33..aa4e1551f0 100644
--- a/Marlin/src/HAL/ESP32/timers.h
+++ b/Marlin/src/HAL/ESP32/timers.h
@@ -24,31 +24,28 @@
 #include 
 #include 
 
-// Includes needed to get I2S_STEPPER_STREAM. Note that pins.h
-// is included in case this header is being included early.
-#include "../../inc/MarlinConfig.h"
-#include "../../pins/pins.h"
-
 // ------------------------
 // Defines
 // ------------------------
-//
 #define FORCE_INLINE __attribute__((always_inline)) inline
 
 typedef uint64_t hal_timer_t;
 #define HAL_TIMER_TYPE_MAX 0xFFFFFFFFFFFFFFFFULL
 
-#ifndef STEP_TIMER_NUM
-  #define STEP_TIMER_NUM        0  // Timer Index for Stepper
+#ifndef MF_TIMER_STEP
+  #define MF_TIMER_STEP         0  // Timer Index for Stepper
 #endif
-#ifndef PULSE_TIMER_NUM
-  #define PULSE_TIMER_NUM       STEP_TIMER_NUM
+#ifndef MF_TIMER_PULSE
+  #define MF_TIMER_PULSE        MF_TIMER_STEP
 #endif
-#ifndef TEMP_TIMER_NUM
-  #define TEMP_TIMER_NUM        1  // Timer Index for Temperature
+#ifndef MF_TIMER_TEMP
+  #define MF_TIMER_TEMP         1  // Timer Index for Temperature
 #endif
-#ifndef PWM_TIMER_NUM
-  #define PWM_TIMER_NUM         2  // index of timer to use for PWM outputs
+#ifndef MF_TIMER_PWM
+  #define MF_TIMER_PWM          2  // index of timer to use for PWM outputs
+#endif
+#ifndef MF_TIMER_TONE
+  #define MF_TIMER_TONE         3  // index of timer for beeper tones
 #endif
 
 #define HAL_TIMER_RATE APB_CLK_FREQ // frequency of timer peripherals
@@ -65,6 +62,8 @@ typedef uint64_t hal_timer_t;
 
 #define STEP_TIMER_MIN_INTERVAL   8 // minimum time in µs between stepper interrupts
 
+#define TONE_TIMER_PRESCALE    1000 // Arbitrary value, no idea what i'm doing here
+
 #define TEMP_TIMER_PRESCALE    1000 // prescaler for setting Temp timer, 72Khz
 #define TEMP_TIMER_FREQUENCY   1000 // temperature interrupt frequency
 
@@ -80,12 +79,12 @@ typedef uint64_t hal_timer_t;
 #define PULSE_TIMER_PRESCALE     STEPPER_TIMER_PRESCALE
 #define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US
 
-#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(STEP_TIMER_NUM)
-#define DISABLE_STEPPER_DRIVER_INTERRUPT()  HAL_timer_disable_interrupt(STEP_TIMER_NUM)
-#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM)
+#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_STEP)
+#define DISABLE_STEPPER_DRIVER_INTERRUPT()  HAL_timer_disable_interrupt(MF_TIMER_STEP)
+#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(MF_TIMER_STEP)
 
-#define ENABLE_TEMPERATURE_INTERRUPT()  HAL_timer_enable_interrupt(TEMP_TIMER_NUM)
-#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM)
+#define ENABLE_TEMPERATURE_INTERRUPT()  HAL_timer_enable_interrupt(MF_TIMER_TEMP)
+#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_TEMP)
 
 #ifndef HAL_TEMP_TIMER_ISR
   #define HAL_TEMP_TIMER_ISR() extern "C" void tempTC_Handler()
@@ -96,10 +95,16 @@ typedef uint64_t hal_timer_t;
 #ifndef HAL_PWM_TIMER_ISR
   #define HAL_PWM_TIMER_ISR() extern "C" void pwmTC_Handler()
 #endif
+#ifndef HAL_TONE_TIMER_ISR
+  #define HAL_TONE_TIMER_ISR() extern "C" void toneTC_Handler()
+#endif
 
-extern "C" void tempTC_Handler();
-extern "C" void stepTC_Handler();
-extern "C" void pwmTC_Handler();
+extern "C" {
+  void tempTC_Handler();
+  void stepTC_Handler();
+  void pwmTC_Handler();
+  void toneTC_Handler();
+}
 
 // ------------------------
 // Types
@@ -116,13 +121,13 @@ typedef struct {
 // Public Variables
 // ------------------------
 
-extern const tTimerConfig TimerConfig[];
+extern const tTimerConfig timer_config[];
 
 // ------------------------
 // Public functions
 // ------------------------
 
-void HAL_timer_start (const uint8_t timer_num, uint32_t frequency);
+void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency);
 void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t count);
 hal_timer_t HAL_timer_get_compare(const uint8_t timer_num);
 hal_timer_t HAL_timer_get_count(const uint8_t timer_num);
@@ -131,5 +136,5 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num);
 void HAL_timer_disable_interrupt(const uint8_t timer_num);
 bool HAL_timer_interrupt_enabled(const uint8_t timer_num);
 
-#define HAL_timer_isr_prologue(TIMER_NUM)
-#define HAL_timer_isr_epilogue(TIMER_NUM)
+#define HAL_timer_isr_prologue(T) NOOP
+#define HAL_timer_isr_epilogue(T) NOOP
diff --git a/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp b/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp
new file mode 100644
index 0000000000..bd7ecdc9f2
--- /dev/null
+++ b/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp
@@ -0,0 +1,106 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * Copypaste of SAMD51 HAL developed by Giuliano Zaro (AKA GMagician)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ *
+ */
+#ifdef ARDUINO_ARCH_ESP32
+
+#include "../../inc/MarlinConfig.h"
+
+#if EITHER(MKS_MINI_12864, FYSETC_MINI_12864_2_1)
+
+#include 
+#include "../shared/HAL_SPI.h"
+#include "HAL.h"
+#include "SPI.h"
+
+#if ENABLED(SDSUPPORT)
+  #include "../../sd/cardreader.h"
+  #if ENABLED(ESP3D_WIFISUPPORT)
+    #include "sd_ESP32.h"
+  #endif
+#endif
+
+static SPISettings spiConfig;
+
+
+#ifndef LCD_SPI_SPEED
+  #ifdef SD_SPI_SPEED
+    #define LCD_SPI_SPEED SD_SPI_SPEED    // Assume SPI speed shared with SD
+  #else
+    #define LCD_SPI_SPEED SPI_FULL_SPEED  // Use full speed if SD speed is not supplied
+  #endif
+#endif
+
+uint8_t u8g_eps_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) {
+  static uint8_t msgInitCount = 2; // Ignore all messages until 2nd U8G_COM_MSG_INIT
+
+  #if ENABLED(PAUSE_LCD_FOR_BUSY_SD)
+    if (card.flag.saving || card.flag.logging || TERN0(ESP3D_WIFISUPPORT, sd_busy_lock == true)) return 0;
+  #endif
+
+  if (msgInitCount) {
+    if (msg == U8G_COM_MSG_INIT) msgInitCount--;
+    if (msgInitCount) return -1;
+  }
+
+  switch (msg) {
+    case U8G_COM_MSG_STOP: break;
+
+    case U8G_COM_MSG_INIT:
+      OUT_WRITE(DOGLCD_CS, HIGH);
+      OUT_WRITE(DOGLCD_A0, HIGH);
+      OUT_WRITE(LCD_RESET_PIN, HIGH);
+      u8g_Delay(5);
+      spiBegin();
+      spiInit(LCD_SPI_SPEED);
+      break;
+
+    case U8G_COM_MSG_ADDRESS:           /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
+      WRITE(DOGLCD_A0, arg_val ? HIGH : LOW);
+      break;
+
+    case U8G_COM_MSG_CHIP_SELECT:       /* arg_val == 0 means HIGH level of U8G_PI_CS */
+      WRITE(DOGLCD_CS, arg_val ? LOW : HIGH);
+      break;
+
+    case U8G_COM_MSG_RESET:
+      WRITE(LCD_RESET_PIN, arg_val);
+      break;
+
+    case U8G_COM_MSG_WRITE_BYTE:
+      spiSend((uint8_t)arg_val);
+      break;
+
+    case U8G_COM_MSG_WRITE_SEQ:
+      uint8_t *ptr = (uint8_t*) arg_ptr;
+      while (arg_val > 0) {
+        spiSend(*ptr++);
+        arg_val--;
+      }
+      break;
+  }
+  return 1;
+}
+
+#endif // EITHER(MKS_MINI_12864, FYSETC_MINI_12864_2_1)
+
+#endif // ARDUINO_ARCH_ESP32
diff --git a/Marlin/src/HAL/ESP32/wifi.cpp b/Marlin/src/HAL/ESP32/wifi.cpp
index f4cf5a606a..060f3bdb48 100644
--- a/Marlin/src/HAL/ESP32/wifi.cpp
+++ b/Marlin/src/HAL/ESP32/wifi.cpp
@@ -59,7 +59,7 @@ void wifi_init() {
 
   MDNS.addService("http", "tcp", 80);
 
-  SERIAL_ECHOLNPAIR("Successfully connected to WiFi with SSID '" WIFI_SSID "', hostname: '" WIFI_HOSTNAME "', IP address: ", WiFi.localIP().toString().c_str());
+  SERIAL_ECHOLNPGM("Successfully connected to WiFi with SSID '" WIFI_SSID "', hostname: '" WIFI_HOSTNAME "', IP address: ", WiFi.localIP().toString().c_str());
 }
 
 #endif // WIFISUPPORT
diff --git a/Marlin/src/HAL/HAL.h b/Marlin/src/HAL/HAL.h
index 5eca2f7eac..5186578019 100644
--- a/Marlin/src/HAL/HAL.h
+++ b/Marlin/src/HAL/HAL.h
@@ -23,14 +23,13 @@
 
 #include "platforms.h"
 
-#include HAL_PATH(.,HAL.h)
-
-#ifdef SERIAL_PORT_2
-  #define NUM_SERIAL 2
-#else
-  #define NUM_SERIAL 1
+#ifndef GCC_VERSION
+  #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
 #endif
 
+#include HAL_PATH(.,HAL.h)
+extern MarlinHAL hal;
+
 #define HAL_ADC_RANGE _BV(HAL_ADC_RESOLUTION)
 
 #ifndef I2C_ADDRESS
@@ -46,7 +45,3 @@
 #ifndef PGMSTR
   #define PGMSTR(NAM,STR) const char NAM[] = STR
 #endif
-
-inline void watchdog_refresh() {
-  TERN_(USE_WATCHDOG, HAL_watchdog_refresh());
-}
diff --git a/Marlin/src/HAL/LINUX/HAL.cpp b/Marlin/src/HAL/LINUX/HAL.cpp
index d7d7c2d2b4..db43f42eaa 100644
--- a/Marlin/src/HAL/LINUX/HAL.cpp
+++ b/Marlin/src/HAL/LINUX/HAL.cpp
@@ -24,58 +24,38 @@
 #include "../../inc/MarlinConfig.h"
 #include "../shared/Delay.h"
 
-HalSerial usb_serial;
+// ------------------------
+// Serial ports
+// ------------------------
+
+MSerialT usb_serial(TERN0(EMERGENCY_PARSER, true));
 
 // U8glib required functions
-extern "C" void u8g_xMicroDelay(uint16_t val) {
-  DELAY_US(val);
-}
-extern "C" void u8g_MicroDelay() {
-  u8g_xMicroDelay(1);
-}
-extern "C" void u8g_10MicroDelay() {
-  u8g_xMicroDelay(10);
-}
-extern "C" void u8g_Delay(uint16_t val) {
-  delay(val);
+extern "C" {
+  void u8g_xMicroDelay(uint16_t val) { DELAY_US(val); }
+  void u8g_MicroDelay()              { u8g_xMicroDelay(1); }
+  void u8g_10MicroDelay()            { u8g_xMicroDelay(10); }
+  void u8g_Delay(uint16_t val)       { delay(val); }
 }
+
 //************************//
 
 // return free heap space
-int freeMemory() {
-  return 0;
-}
+int freeMemory() { return 0; }
 
 // ------------------------
 // ADC
 // ------------------------
 
-void HAL_adc_init() {
+uint8_t MarlinHAL::active_ch = 0;
 
-}
-
-void HAL_adc_enable_channel(const uint8_t ch) {
-
-}
-
-uint8_t active_ch = 0;
-void HAL_adc_start_conversion(const uint8_t ch) {
-  active_ch = ch;
-}
-
-bool HAL_adc_finished() {
-  return true;
-}
-
-uint16_t HAL_adc_get_result() {
-  pin_t pin = analogInputToDigitalPin(active_ch);
+uint16_t MarlinHAL::adc_value() {
+  const pin_t pin = analogInputToDigitalPin(active_ch);
   if (!VALID_PIN(pin)) return 0;
-  uint16_t data = ((Gpio::get(pin) >> 2) & 0x3FF);
+  const uint16_t data = ((Gpio::get(pin) >> 2) & 0x3FF);
   return data;    // return 10bit value as Marlin expects
 }
 
-void HAL_pwm_init() {
-
-}
+void MarlinHAL::reboot() { /* Reset the application state and GPIO */ }
 
 #endif // __PLAT_LINUX__
diff --git a/Marlin/src/HAL/LINUX/HAL.h b/Marlin/src/HAL/LINUX/HAL.h
index 2e545e03d6..22c3e521f0 100644
--- a/Marlin/src/HAL/LINUX/HAL.h
+++ b/Marlin/src/HAL/LINUX/HAL.h
@@ -21,25 +21,42 @@
  */
 #pragma once
 
-#define CPU_32_BIT
+#include "../../inc/MarlinConfigPre.h"
 
-#define F_CPU 100000000
-#define SystemCoreClock F_CPU
 #include 
 #include 
 #include 
-
 #undef min
 #undef max
-
 #include 
 
-void _printf (const  char *format, ...);
+#include "hardware/Clock.h"
+#include "../shared/Marduino.h"
+#include "../shared/math_32bit.h"
+#include "../shared/HAL_SPI.h"
+#include "fastio.h"
+#include "serial.h"
+
+// ------------------------
+// Defines
+// ------------------------
+
+#define CPU_32_BIT
+#define SHARED_SERVOS HAS_SERVOS  // Use shared/servos.cpp
+
+#define F_CPU 100000000UL
+#define SystemCoreClock F_CPU
+
+#define DELAY_CYCLES(x) Clock::delayCycles(x)
+
+#define CPU_ST7920_DELAY_1 600
+#define CPU_ST7920_DELAY_2 750
+#define CPU_ST7920_DELAY_3 750
+
+void _printf(const  char *format, ...);
 void _putc(uint8_t c);
 uint8_t _getc();
 
-//extern "C" volatile uint32_t _millis;
-
 //arduino: Print.h
 #define DEC 10
 #define HEX 16
@@ -49,66 +66,100 @@ uint8_t _getc();
 #define B01 1
 #define B10 2
 
-#include "hardware/Clock.h"
+// ------------------------
+// Serial ports
+// ------------------------
 
-#include "../shared/Marduino.h"
-#include "../shared/math_32bit.h"
-#include "../shared/HAL_SPI.h"
-#include "fastio.h"
-#include "watchdog.h"
-#include "serial.h"
-
-#define SHARED_SERVOS HAS_SERVOS
-
-extern HalSerial usb_serial;
-#define MYSERIAL0 usb_serial
-
-#define ST7920_DELAY_1 DELAY_NS(600)
-#define ST7920_DELAY_2 DELAY_NS(750)
-#define ST7920_DELAY_3 DELAY_NS(750)
+extern MSerialT usb_serial;
+#define MYSERIAL1 usb_serial
 
 //
 // Interrupts
 //
 #define CRITICAL_SECTION_START()
 #define CRITICAL_SECTION_END()
-#define ISRS_ENABLED()
-#define ENABLE_ISRS()
-#define DISABLE_ISRS()
-
-inline void HAL_init() {}
-
-// Utility functions
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-int freeMemory();
-#pragma GCC diagnostic pop
 
 // ADC
 #define HAL_ADC_VREF           5.0
 #define HAL_ADC_RESOLUTION    10
-#define HAL_ANALOG_SELECT(ch) HAL_adc_enable_channel(ch)
-#define HAL_START_ADC(ch)     HAL_adc_start_conversion(ch)
-#define HAL_READ_ADC()        HAL_adc_get_result()
-#define HAL_ADC_READY()       true
 
-void HAL_adc_init();
-void HAL_adc_enable_channel(const uint8_t ch);
-void HAL_adc_start_conversion(const uint8_t ch);
-uint16_t HAL_adc_get_result();
+// ------------------------
+// Class Utilities
+// ------------------------
 
-// Reset source
-inline void HAL_clear_reset_source(void) {}
-inline uint8_t HAL_get_reset_source(void) { return RST_POWER_ON; }
-
-inline void HAL_reboot() {}  // reboot the board or restart the bootloader
-
-/* ---------------- Delay in cycles */
-FORCE_INLINE static void DELAY_CYCLES(uint64_t x) {
-  Clock::delayCycles(x);
-}
-
-// Add strcmp_P if missing
-#ifndef strcmp_P
-  #define strcmp_P(a, b) strcmp((a), (b))
+#pragma GCC diagnostic push
+#if GCC_VERSION <= 50000
+  #pragma GCC diagnostic ignored "-Wunused-function"
 #endif
+
+int freeMemory();
+
+#pragma GCC diagnostic pop
+
+// ------------------------
+// MarlinHAL Class
+// ------------------------
+
+class MarlinHAL {
+public:
+
+  // Earliest possible init, before setup()
+  MarlinHAL() {}
+
+  // Watchdog
+  static void watchdog_init() {}
+  static void watchdog_refresh() {}
+
+  static void init() {}        // Called early in setup()
+  static void init_board() {}  // Called less early in setup()
+  static void reboot();        // Reset the application state and GPIO
+
+  // Interrupts
+  static bool isr_state() { return true; }
+  static void isr_on()  {}
+  static void isr_off() {}
+
+  static void delay_ms(const int ms) { _delay_ms(ms); }
+
+  // Tasks, called from idle()
+  static void idletask() {}
+
+  // Reset
+  static constexpr uint8_t reset_reason = RST_POWER_ON;
+  static uint8_t get_reset_source() { return reset_reason; }
+  static void clear_reset_source() {}
+
+  // Free SRAM
+  static int freeMemory() { return ::freeMemory(); }
+
+  //
+  // ADC Methods
+  //
+
+  static uint8_t active_ch;
+
+  // Called by Temperature::init once at startup
+  static void adc_init() {}
+
+  // Called by Temperature::init for each sensor at startup
+  static void adc_enable(const uint8_t) {}
+
+  // Begin ADC sampling on the given channel
+  static void adc_start(const uint8_t ch) { active_ch = ch; }
+
+  // Is the ADC ready for reading?
+  static bool adc_ready() { return true; }
+
+  // The current value of the ADC register
+  static uint16_t adc_value();
+
+  /**
+   * Set the PWM duty cycle for the pin to the given value.
+   * No option to change the resolution or invert the duty cycle.
+   */
+  static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) {
+    analogWrite(pin, v);
+  }
+
+  static void set_pwm_frequency(const pin_t, int) {}
+};
diff --git a/Marlin/src/HAL/LINUX/MarlinSPI.h b/Marlin/src/HAL/LINUX/MarlinSPI.h
new file mode 100644
index 0000000000..0c447ba4cb
--- /dev/null
+++ b/Marlin/src/HAL/LINUX/MarlinSPI.h
@@ -0,0 +1,26 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ *
+ */
+#pragma once
+
+#include 
+
+using MarlinSPI = SPIClass;
diff --git a/Marlin/src/HAL/LINUX/arduino.cpp b/Marlin/src/HAL/LINUX/arduino.cpp
index 4b56d02a38..075b4ccde2 100644
--- a/Marlin/src/HAL/LINUX/arduino.cpp
+++ b/Marlin/src/HAL/LINUX/arduino.cpp
@@ -31,9 +31,7 @@ void cli() { } // Disable
 void sei() { } // Enable
 
 // Time functions
-void _delay_ms(const int delay_ms) {
-  delay(delay_ms);
-}
+void _delay_ms(const int ms) { delay(ms); }
 
 uint32_t millis() {
   return (uint32_t)Clock::millis();
diff --git a/Marlin/src/HAL/LINUX/eeprom.cpp b/Marlin/src/HAL/LINUX/eeprom.cpp
index 967ca851ab..f878bba6a5 100644
--- a/Marlin/src/HAL/LINUX/eeprom.cpp
+++ b/Marlin/src/HAL/LINUX/eeprom.cpp
@@ -40,7 +40,7 @@ size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; }
 bool PersistentStore::access_start() {
   const char eeprom_erase_value = 0xFF;
   FILE * eeprom_file = fopen(filename, "rb");
-  if (eeprom_file == nullptr) return false;
+  if (!eeprom_file) return false;
 
   fseek(eeprom_file, 0L, SEEK_END);
   std::size_t file_size = ftell(eeprom_file);
@@ -59,7 +59,7 @@ bool PersistentStore::access_start() {
 
 bool PersistentStore::access_finish() {
   FILE * eeprom_file = fopen(filename, "wb");
-  if (eeprom_file == nullptr) return false;
+  if (!eeprom_file) return false;
   fwrite(buffer, sizeof(uint8_t), sizeof(buffer), eeprom_file);
   fclose(eeprom_file);
   return true;
@@ -69,34 +69,34 @@ bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, ui
   std::size_t bytes_written = 0;
 
   for (std::size_t i = 0; i < size; i++) {
-    buffer[pos+i] = value[i];
-    bytes_written ++;
+    buffer[pos + i] = value[i];
+    bytes_written++;
   }
 
   crc16(crc, value, size);
-  pos = pos + size;
+  pos += size;
   return (bytes_written != size);  // return true for any error
 }
 
-bool PersistentStore::read_data(int &pos, uint8_t* value, const size_t size, uint16_t *crc, const bool writing/*=true*/) {
+bool PersistentStore::read_data(int &pos, uint8_t *value, const size_t size, uint16_t *crc, const bool writing/*=true*/) {
   std::size_t bytes_read = 0;
   if (writing) {
     for (std::size_t i = 0; i < size; i++) {
-      value[i] = buffer[pos+i];
-      bytes_read ++;
+      value[i] = buffer[pos + i];
+      bytes_read++;
     }
     crc16(crc, value, size);
   }
   else {
     uint8_t temp[size];
     for (std::size_t i = 0; i < size; i++) {
-      temp[i] = buffer[pos+i];
-      bytes_read ++;
+      temp[i] = buffer[pos + i];
+      bytes_read++;
     }
     crc16(crc, temp, size);
   }
 
-  pos = pos + size;
+  pos += size;
   return bytes_read != size;  // return true for any error
 }
 
diff --git a/Marlin/src/HAL/LINUX/hardware/Gpio.h b/Marlin/src/HAL/LINUX/hardware/Gpio.h
index 9255ec1dfc..f946be6484 100644
--- a/Marlin/src/HAL/LINUX/hardware/Gpio.h
+++ b/Marlin/src/HAL/LINUX/hardware/Gpio.h
@@ -40,7 +40,7 @@ struct GpioEvent {
   pin_type pin_id;
   GpioEvent::Type event;
 
-  GpioEvent(uint64_t timestamp, pin_type pin_id, GpioEvent::Type event){
+  GpioEvent(uint64_t timestamp, pin_type pin_id, GpioEvent::Type event) {
     this->timestamp = timestamp;
     this->pin_id = pin_id;
     this->event = event;
@@ -86,10 +86,10 @@ public:
     GpioEvent::Type evt_type = value > 1 ? GpioEvent::SET_VALUE : value > pin_map[pin].value ? GpioEvent::RISE : value < pin_map[pin].value ? GpioEvent::FALL : GpioEvent::NOP;
     pin_map[pin].value = value;
     GpioEvent evt(Clock::nanos(), pin, evt_type);
-    if (pin_map[pin].cb != nullptr) {
+    if (pin_map[pin].cb) {
       pin_map[pin].cb->interrupt(evt);
     }
-    if (Gpio::logger != nullptr) Gpio::logger->log(evt);
+    if (Gpio::logger) Gpio::logger->log(evt);
   }
 
   static uint16_t get(pin_type pin) {
@@ -105,8 +105,8 @@ public:
     if (!valid_pin(pin)) return;
     pin_map[pin].mode = value;
     GpioEvent evt(Clock::nanos(), pin, GpioEvent::Type::SETM);
-    if (pin_map[pin].cb != nullptr) pin_map[pin].cb->interrupt(evt);
-    if (Gpio::logger != nullptr) Gpio::logger->log(evt);
+    if (pin_map[pin].cb) pin_map[pin].cb->interrupt(evt);
+    if (Gpio::logger) Gpio::logger->log(evt);
   }
 
   static uint8_t getMode(pin_type pin) {
@@ -118,8 +118,8 @@ public:
     if (!valid_pin(pin)) return;
     pin_map[pin].dir = value;
     GpioEvent evt(Clock::nanos(), pin, GpioEvent::Type::SETD);
-    if (pin_map[pin].cb != nullptr) pin_map[pin].cb->interrupt(evt);
-    if (Gpio::logger != nullptr) Gpio::logger->log(evt);
+    if (pin_map[pin].cb) pin_map[pin].cb->interrupt(evt);
+    if (Gpio::logger) Gpio::logger->log(evt);
   }
 
   static uint8_t getDir(pin_type pin) {
diff --git a/Marlin/src/HAL/LINUX/hardware/Heater.cpp b/Marlin/src/HAL/LINUX/hardware/Heater.cpp
index 70df816182..44f11986c9 100644
--- a/Marlin/src/HAL/LINUX/hardware/Heater.cpp
+++ b/Marlin/src/HAL/LINUX/hardware/Heater.cpp
@@ -54,7 +54,7 @@ void Heater::update() {
 }
 
 void Heater::interrupt(GpioEvent ev) {
-  // ununsed
+  // unused
 }
 
 #endif // __PLAT_LINUX__
diff --git a/Marlin/src/HAL/LINUX/hardware/Heater.h b/Marlin/src/HAL/LINUX/hardware/Heater.h
index b17078d0b7..6d590ce6c5 100644
--- a/Marlin/src/HAL/LINUX/hardware/Heater.h
+++ b/Marlin/src/HAL/LINUX/hardware/Heater.h
@@ -26,8 +26,8 @@
 struct LowpassFilter {
   uint64_t data_delay = 0;
   uint16_t update(uint16_t value) {
-    data_delay = data_delay - (data_delay >> 6) + value;
-    return (uint16_t)(data_delay >> 6);
+    data_delay += value - (data_delay >> 6);
+    return uint16_t(data_delay >> 6);
   }
 };
 
diff --git a/Marlin/src/HAL/LINUX/hardware/LinearAxis.cpp b/Marlin/src/HAL/LINUX/hardware/LinearAxis.cpp
index c5b3ccc986..e122ef3666 100644
--- a/Marlin/src/HAL/LINUX/hardware/LinearAxis.cpp
+++ b/Marlin/src/HAL/LINUX/hardware/LinearAxis.cpp
@@ -51,7 +51,7 @@ void LinearAxis::update() {
 }
 
 void LinearAxis::interrupt(GpioEvent ev) {
-  if (ev.pin_id == step_pin && !Gpio::pin_map[enable_pin].value){
+  if (ev.pin_id == step_pin && !Gpio::pin_map[enable_pin].value) {
     if (ev.event == GpioEvent::RISE) {
       last_update = ev.timestamp;
       position += -1 + 2 * Gpio::pin_map[dir_pin].value;
diff --git a/Marlin/src/HAL/LINUX/hardware/Timer.h b/Marlin/src/HAL/LINUX/hardware/Timer.h
index 757efdcdbd..1b3b800dca 100644
--- a/Marlin/src/HAL/LINUX/hardware/Timer.h
+++ b/Marlin/src/HAL/LINUX/hardware/Timer.h
@@ -52,7 +52,7 @@ public:
     return (*(intptr_t*)timerid);
   }
 
-  static void handler(int sig, siginfo_t *si, void *uc){
+  static void handler(int sig, siginfo_t *si, void *uc) {
     Timer* _this = (Timer*)si->si_value.sival_ptr;
     _this->avg_error += (Clock::nanos() - _this->start_time) - _this->period; //high_resolution_clock is also limited in precision, but best we have
     _this->avg_error /= 2; //very crude precision analysis (actually within +-500ns usually)
diff --git a/Marlin/src/HAL/LINUX/inc/SanityCheck.h b/Marlin/src/HAL/LINUX/inc/SanityCheck.h
index 84167c97a1..36d3190a3e 100644
--- a/Marlin/src/HAL/LINUX/inc/SanityCheck.h
+++ b/Marlin/src/HAL/LINUX/inc/SanityCheck.h
@@ -26,7 +26,7 @@
  */
 
 // Emulating RAMPS
-#if ENABLED(SPINDLE_LASER_PWM) && !(SPINDLE_LASER_PWM_PIN == 4 || SPINDLE_LASER_PWM_PIN == 6 || SPINDLE_LASER_PWM_PIN == 11)
+#if ENABLED(SPINDLE_LASER_USE_PWM) && !(SPINDLE_LASER_PWM_PIN == 4 || SPINDLE_LASER_PWM_PIN == 6 || SPINDLE_LASER_PWM_PIN == 11)
   #error "SPINDLE_LASER_PWM_PIN must use SERVO0, SERVO1 or SERVO3 connector"
 #endif
 
@@ -35,5 +35,9 @@
 #endif
 
 #if HAS_TMC_SW_SERIAL
-  #error "TMC220x Software Serial is not supported on this platform."
+  #error "TMC220x Software Serial is not supported on LINUX."
+#endif
+
+#if ENABLED(POSTMORTEM_DEBUGGING)
+  #error "POSTMORTEM_DEBUGGING is not yet supported on LINUX."
 #endif
diff --git a/Marlin/src/HAL/LINUX/include/Arduino.h b/Marlin/src/HAL/LINUX/include/Arduino.h
index e28b474ede..f05aaed880 100644
--- a/Marlin/src/HAL/LINUX/include/Arduino.h
+++ b/Marlin/src/HAL/LINUX/include/Arduino.h
@@ -59,43 +59,22 @@ typedef uint8_t byte;
 #endif
 
 #define sq(v) ((v) * (v))
-#define square(v) sq(v)
 #define constrain(value, arg_min, arg_max) ((value) < (arg_min) ? (arg_min) :((value) > (arg_max) ? (arg_max) : (value)))
 
-//Interrupts
+// Interrupts
 void cli(); // Disable
 void sei(); // Enable
 void attachInterrupt(uint32_t pin, void (*callback)(), uint32_t mode);
 void detachInterrupt(uint32_t pin);
-extern "C" void GpioEnableInt(uint32_t port, uint32_t pin, uint32_t mode);
-extern "C" void GpioDisableInt(uint32_t port, uint32_t pin);
 
-// Program Memory
-#define pgm_read_ptr(addr)        (*((void**)(addr)))
-#define pgm_read_byte_near(addr)  (*((uint8_t*)(addr)))
-#define pgm_read_float_near(addr) (*((float*)(addr)))
-#define pgm_read_word_near(addr)  (*((uint16_t*)(addr)))
-#define pgm_read_dword_near(addr) (*((uint32_t*)(addr)))
-#define pgm_read_byte(addr)       pgm_read_byte_near(addr)
-#define pgm_read_float(addr)      pgm_read_float_near(addr)
-#define pgm_read_word(addr)       pgm_read_word_near(addr)
-#define pgm_read_dword(addr)      pgm_read_dword_near(addr)
-
-using std::memcpy;
-#define memcpy_P memcpy
-#define sprintf_P sprintf
-#define strstr_P strstr
-#define strncpy_P strncpy
-#define vsnprintf_P vsnprintf
-#define strcpy_P strcpy
-#define snprintf_P snprintf
-#define strlen_P strlen
+extern "C" {
+  void GpioEnableInt(uint32_t port, uint32_t pin, uint32_t mode);
+  void GpioDisableInt(uint32_t port, uint32_t pin);
+}
 
 // Time functions
-extern "C" {
-  void delay(const int milis);
-}
-void _delay_ms(const int delay);
+extern "C" void delay(const int ms);
+void _delay_ms(const int ms);
 void delayMicroseconds(unsigned long);
 uint32_t millis();
 
diff --git a/Marlin/src/HAL/LINUX/include/pinmapping.cpp b/Marlin/src/HAL/LINUX/include/pinmapping.cpp
index 870ab3a96e..5823668cd5 100644
--- a/Marlin/src/HAL/LINUX/include/pinmapping.cpp
+++ b/Marlin/src/HAL/LINUX/include/pinmapping.cpp
@@ -25,43 +25,6 @@
 
 #include "../../../gcode/parser.h"
 
-uint8_t analog_offset = NUM_DIGITAL_PINS - NUM_ANALOG_INPUTS;
-
-// Get the digital pin for an analog index
-pin_t analogInputToDigitalPin(const int8_t p) {
-  return (WITHIN(p, 0, NUM_ANALOG_INPUTS) ? analog_offset + p : P_NC);
-}
-
-// Return the index of a pin number
-int16_t GET_PIN_MAP_INDEX(const pin_t pin) {
-  return pin;
-}
-
-// Test whether the pin is valid
-bool VALID_PIN(const pin_t p) {
-  return WITHIN(p, 0, NUM_DIGITAL_PINS);
-}
-
-// Get the analog index for a digital pin
-int8_t DIGITAL_PIN_TO_ANALOG_PIN(const pin_t p) {
-  return (WITHIN(p, analog_offset, NUM_DIGITAL_PINS) ? p - analog_offset : P_NC);
-}
-
-// Test whether the pin is PWM
-bool PWM_PIN(const pin_t p) {
-  return false;
-}
-
-// Test whether the pin is interruptable
-bool INTERRUPT_PIN(const pin_t p) {
-  return false;
-}
-
-// Get the pin number at the given index
-pin_t GET_PIN_MAP_PIN(const int16_t ind) {
-  return ind;
-}
-
 int16_t PARSED_PIN_INDEX(const char code, const int16_t dval) {
   return parser.intval(code, dval);
 }
diff --git a/Marlin/src/HAL/LINUX/include/pinmapping.h b/Marlin/src/HAL/LINUX/include/pinmapping.h
index 98f4b812e8..cfac5e3b48 100644
--- a/Marlin/src/HAL/LINUX/include/pinmapping.h
+++ b/Marlin/src/HAL/LINUX/include/pinmapping.h
@@ -34,26 +34,32 @@ constexpr uint8_t NUM_ANALOG_INPUTS = 16;
 
 #define HAL_SENSITIVE_PINS
 
+constexpr uint8_t analog_offset = NUM_DIGITAL_PINS - NUM_ANALOG_INPUTS;
+
 // Get the digital pin for an analog index
-pin_t analogInputToDigitalPin(const int8_t p);
-
-// Return the index of a pin number
-int16_t GET_PIN_MAP_INDEX(const pin_t pin);
-
-// Test whether the pin is valid
-bool VALID_PIN(const pin_t p);
+constexpr pin_t analogInputToDigitalPin(const int8_t p) {
+  return (WITHIN(p, 0, NUM_ANALOG_INPUTS) ? analog_offset + p : P_NC);
+}
 
 // Get the analog index for a digital pin
-int8_t DIGITAL_PIN_TO_ANALOG_PIN(const pin_t p);
+constexpr int8_t DIGITAL_PIN_TO_ANALOG_PIN(const pin_t p) {
+  return (WITHIN(p, analog_offset, NUM_DIGITAL_PINS) ? p - analog_offset : P_NC);
+}
+
+// Return the index of a pin number
+constexpr int16_t GET_PIN_MAP_INDEX(const pin_t pin) { return pin; }
+
+// Test whether the pin is valid
+constexpr bool VALID_PIN(const pin_t p) { return WITHIN(p, 0, NUM_DIGITAL_PINS); }
 
 // Test whether the pin is PWM
-bool PWM_PIN(const pin_t p);
+constexpr bool PWM_PIN(const pin_t p) { return false; }
 
-// Test whether the pin is interruptable
-bool INTERRUPT_PIN(const pin_t p);
+// Test whether the pin is interruptible
+constexpr bool INTERRUPT_PIN(const pin_t p) { return false; }
 
 // Get the pin number at the given index
-pin_t GET_PIN_MAP_PIN(const int16_t ind);
+constexpr pin_t GET_PIN_MAP_PIN(const int16_t ind) { return ind; }
 
 // Parse a G-code word into a pin index
 int16_t PARSED_PIN_INDEX(const char code, const int16_t dval);
diff --git a/Marlin/src/HAL/LINUX/include/serial.h b/Marlin/src/HAL/LINUX/include/serial.h
index e916249389..ebae066c3a 100644
--- a/Marlin/src/HAL/LINUX/include/serial.h
+++ b/Marlin/src/HAL/LINUX/include/serial.h
@@ -25,6 +25,7 @@
 #if ENABLED(EMERGENCY_PARSER)
   #include "../../../feature/e_parser.h"
 #endif
+#include "../../../core/serial_hook.h"
 
 #include 
 #include 
@@ -73,19 +74,11 @@ private:
   volatile uint32_t index_read;
 };
 
-class HalSerial {
-public:
-
-  #if ENABLED(EMERGENCY_PARSER)
-    EmergencyParser::State emergency_state;
-    static inline bool emergency_parser_enabled() { return true; }
-  #endif
-
+struct HalSerial {
   HalSerial() { host_connected = true; }
 
   void begin(int32_t) {}
-
-  void end() {}
+  void end()          {}
 
   int peek() {
     uint8_t value;
@@ -100,7 +93,7 @@ public:
     return transmit_buffer.write(c);
   }
 
-  operator bool() { return host_connected; }
+  bool connected() { return host_connected; }
 
   uint16_t available() {
     return (uint16_t)receive_buffer.available();
@@ -117,92 +110,9 @@ public:
       while (transmit_buffer.available()) { /* nada */ }
   }
 
-  void printf(const char *format, ...) {
-    static char buffer[256];
-    va_list vArgs;
-    va_start(vArgs, format);
-    int length = vsnprintf((char *) buffer, 256, (char const *) format, vArgs);
-    va_end(vArgs);
-    if (length > 0 && length < 256) {
-      if (host_connected) {
-        for (int i = 0; i < length;) {
-          if (transmit_buffer.write(buffer[i])) {
-            ++i;
-          }
-        }
-      }
-    }
-  }
-
-  #define DEC 10
-  #define HEX 16
-  #define OCT 8
-  #define BIN 2
-
-  void print_bin(uint32_t value, uint8_t num_digits) {
-    uint32_t mask = 1 << (num_digits -1);
-    for (uint8_t i = 0; i < num_digits; i++) {
-      if (!(i %  4) && i) write(' ');
-      if (!(i % 16) && i) write(' ');
-      if (value & mask)   write('1');
-      else                write('0');
-      value <<= 1;
-    }
-  }
-
-  void print(const char value[]) { printf("%s" , value); }
-  void print(char value, int nbase = 0) {
-    if (nbase == BIN) print_bin(value, 8);
-    else if (nbase == OCT) printf("%3o", value);
-    else if (nbase == HEX) printf("%2X", value);
-    else if (nbase == DEC ) printf("%d", value);
-    else printf("%c" , value);
-  }
-  void print(unsigned char value, int nbase = 0) {
-    if (nbase == BIN) print_bin(value, 8);
-    else if (nbase == OCT) printf("%3o", value);
-    else if (nbase == HEX) printf("%2X", value);
-    else printf("%u" , value);
-  }
-  void print(int value, int nbase = 0) {
-    if (nbase == BIN) print_bin(value, 16);
-    else if (nbase == OCT) printf("%6o", value);
-    else if (nbase == HEX) printf("%4X", value);
-    else printf("%d", value);
-  }
-  void print(unsigned int value, int nbase = 0) {
-    if (nbase == BIN) print_bin(value, 16);
-    else if (nbase == OCT) printf("%6o", value);
-    else if (nbase == HEX) printf("%4X", value);
-    else printf("%u" , value);
-  }
-  void print(long value, int nbase = 0) {
-    if (nbase == BIN) print_bin(value, 32);
-    else if (nbase == OCT) printf("%11o", value);
-    else if (nbase == HEX) printf("%8X", value);
-    else printf("%ld" , value);
-  }
-  void print(unsigned long value, int nbase = 0) {
-    if (nbase == BIN) print_bin(value, 32);
-    else if (nbase == OCT) printf("%11o", value);
-    else if (nbase == HEX) printf("%8X", value);
-    else printf("%lu" , value);
-  }
-  void print(float value, int round = 6)  { printf("%f" , value); }
-  void print(double value, int round = 6) { printf("%f" , value); }
-
-  void println(const char value[]) { printf("%s\n" , value); }
-  void println(char value, int nbase = 0) { print(value, nbase); println(); }
-  void println(unsigned char value, int nbase = 0) { print(value, nbase); println(); }
-  void println(int value, int nbase = 0) { print(value, nbase); println(); }
-  void println(unsigned int value, int nbase = 0) { print(value, nbase); println(); }
-  void println(long value, int nbase = 0) { print(value, nbase); println(); }
-  void println(unsigned long value, int nbase = 0) { print(value, nbase); println(); }
-  void println(float value, int round = 6) { printf("%f\n" , value); }
-  void println(double value, int round = 6) { printf("%f\n" , value); }
-  void println() { print('\n'); }
-
   volatile RingBuffer receive_buffer;
   volatile RingBuffer transmit_buffer;
   volatile bool host_connected;
 };
+
+typedef Serial1Class MSerialT;
diff --git a/Marlin/src/HAL/LINUX/main.cpp b/Marlin/src/HAL/LINUX/main.cpp
index 481f059030..f2af2ff33f 100644
--- a/Marlin/src/HAL/LINUX/main.cpp
+++ b/Marlin/src/HAL/LINUX/main.cpp
@@ -1,8 +1,10 @@
 /**
  * Marlin 3D Printer Firmware
- *
  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
@@ -17,24 +19,26 @@
  * along with this program.  If not, see .
  *
  */
+
 #ifdef __PLAT_LINUX__
 
-extern void setup();
-extern void loop();
-
-#include 
-
-#include 
-#include 
+//#define GPIO_LOGGING // Full GPIO and Positional Logging
 
 #include "../../inc/MarlinConfig.h"
-#include 
-#include 
 #include "../shared/Delay.h"
 #include "hardware/IOLoggerCSV.h"
 #include "hardware/Heater.h"
 #include "hardware/LinearAxis.h"
 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+extern void setup();
+extern void loop();
+
 // simple stdout / stdin implementation for fake serial port
 void write_serial_thread() {
   for (;;) {
@@ -64,8 +68,6 @@ void simulation_loop() {
   LinearAxis z_axis(Z_ENABLE_PIN, Z_DIR_PIN, Z_STEP_PIN, Z_MIN_PIN, Z_MAX_PIN);
   LinearAxis extruder0(E0_ENABLE_PIN, E0_DIR_PIN, E0_STEP_PIN, P_NC, P_NC);
 
-  //#define GPIO_LOGGING // Full GPIO and Positional Logging
-
   #ifdef GPIO_LOGGING
     IOLoggerCSV logger("all_gpio_log.csv");
     Gpio::attachLogger(&logger);
@@ -88,7 +90,7 @@ void simulation_loop() {
 
     #ifdef GPIO_LOGGING
       if (x_axis.position != x || y_axis.position != y || z_axis.position != z) {
-        uint64_t update = MAX3(x_axis.last_update, y_axis.last_update, z_axis.last_update);
+        uint64_t update = _MAX(x_axis.last_update, y_axis.last_update, z_axis.last_update);
         position_log << update << ", " << x_axis.position << ", " << y_axis.position << ", " << z_axis.position << std::endl;
         position_log.flush();
         x = x_axis.position;
@@ -107,8 +109,8 @@ int main() {
   std::thread write_serial (write_serial_thread);
   std::thread read_serial (read_serial_thread);
 
-  #ifdef MYSERIAL0
-    MYSERIAL0.begin(BAUDRATE);
+  #ifdef MYSERIAL1
+    MYSERIAL1.begin(BAUDRATE);
     SERIAL_ECHOLNPGM("x86_64 Initialized");
     SERIAL_FLUSHTX();
   #endif
diff --git a/Marlin/src/HAL/LINUX/pinsDebug.h b/Marlin/src/HAL/LINUX/pinsDebug.h
index a93ceddc61..7bfd97d024 100644
--- a/Marlin/src/HAL/LINUX/pinsDebug.h
+++ b/Marlin/src/HAL/LINUX/pinsDebug.h
@@ -2,6 +2,9 @@
  * Marlin 3D Printer Firmware
  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
@@ -26,15 +29,16 @@
  */
 
 #define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS
-#define pwm_details(pin) pin = pin    // do nothing  // print PWM details
-#define pwm_status(pin) false //Print a pin's PWM status. Return true if it's currently a PWM pin.
-#define IS_ANALOG(P) (DIGITAL_PIN_TO_ANALOG_PIN(P) >= 0 ? 1 : 0)
+#define pwm_details(pin)    NOOP          // (do nothing)
+#define pwm_status(pin)     false         // Print a pin's PWM status. Return true if it's currently a PWM pin.
+#define IS_ANALOG(P)        (DIGITAL_PIN_TO_ANALOG_PIN(P) >= 0 ? 1 : 0)
 #define digitalRead_mod(p)  digitalRead(p)
 #define PRINT_PORT(p)
-#define GET_ARRAY_PIN(p) pin_array[p].pin
+#define GET_ARRAY_PIN(p)    pin_array[p].pin
 #define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0)
-#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0)
-#define MULTI_NAME_PAD 16 // space needed to be pretty if not first name assigned to a pin
+#define PRINT_PIN(p)        do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0)
+#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d)  "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0)
+#define MULTI_NAME_PAD  16 // space needed to be pretty if not first name assigned to a pin
 
 // active ADC function/mode/code values for PINSEL registers
 constexpr int8_t ADC_pin_mode(pin_t pin) {
diff --git a/Marlin/src/HAL/LINUX/spi_pins.h b/Marlin/src/HAL/LINUX/spi_pins.h
index 01ba28e5b6..33136ac9dd 100644
--- a/Marlin/src/HAL/LINUX/spi_pins.h
+++ b/Marlin/src/HAL/LINUX/spi_pins.h
@@ -24,31 +24,32 @@
 #include "../../core/macros.h"
 #include "../../inc/MarlinConfigPre.h"
 
-#if BOTH(HAS_MARLINUI_U8GLIB, SDSUPPORT) && (LCD_PINS_D4 == SCK_PIN || LCD_PINS_ENABLE == MOSI_PIN || DOGLCD_SCK == SCK_PIN || DOGLCD_MOSI == MOSI_PIN)
+#if BOTH(HAS_MARLINUI_U8GLIB, SDSUPPORT) && (LCD_PINS_D4 == SD_SCK_PIN || LCD_PINS_ENABLE == SD_MOSI_PIN || DOGLCD_SCK == SD_SCK_PIN || DOGLCD_MOSI == SD_MOSI_PIN)
   #define LPC_SOFTWARE_SPI  // If the SD card and LCD adapter share the same SPI pins, then software SPI is currently
                             // needed due to the speed and mode required for communicating with each device being different.
                             // This requirement can be removed if the SPI access to these devices is updated to use
                             // spiBeginTransaction.
 #endif
 
-/** onboard SD card */
-//#define SCK_PIN           P0_07
-//#define MISO_PIN          P0_08
-//#define MOSI_PIN          P0_09
-//#define SS_PIN            P0_06
-/** external */
-#ifndef SCK_PIN
-  #define SCK_PIN           50
+// Onboard SD
+//#define SD_SCK_PIN     P0_07
+//#define SD_MISO_PIN    P0_08
+//#define SD_MOSI_PIN    P0_09
+//#define SD_SS_PIN      P0_06
+
+// External SD
+#ifndef SD_SCK_PIN
+  #define SD_SCK_PIN        50
 #endif
-#ifndef MISO_PIN
-  #define MISO_PIN          51
+#ifndef SD_MISO_PIN
+  #define SD_MISO_PIN       51
 #endif
-#ifndef MOSI_PIN
-  #define MOSI_PIN          52
+#ifndef SD_MOSI_PIN
+  #define SD_MOSI_PIN       52
 #endif
-#ifndef SS_PIN
-  #define SS_PIN            53
+#ifndef SD_SS_PIN
+  #define SD_SS_PIN         53
 #endif
 #ifndef SDSS
-  #define SDSS              SS_PIN
+  #define SDSS       SD_SS_PIN
 #endif
diff --git a/Marlin/src/HAL/LINUX/timers.h b/Marlin/src/HAL/LINUX/timers.h
index 1beaea95ab..2d2a95774c 100644
--- a/Marlin/src/HAL/LINUX/timers.h
+++ b/Marlin/src/HAL/LINUX/timers.h
@@ -37,14 +37,14 @@ typedef uint32_t hal_timer_t;
 
 #define HAL_TIMER_RATE         ((SystemCoreClock) / 4)  // frequency of timers peripherals
 
-#ifndef STEP_TIMER_NUM
-  #define STEP_TIMER_NUM        0  // Timer Index for Stepper
+#ifndef MF_TIMER_STEP
+  #define MF_TIMER_STEP         0  // Timer Index for Stepper
 #endif
-#ifndef PULSE_TIMER_NUM
-  #define PULSE_TIMER_NUM       STEP_TIMER_NUM
+#ifndef MF_TIMER_PULSE
+  #define MF_TIMER_PULSE        MF_TIMER_STEP
 #endif
-#ifndef TEMP_TIMER_NUM
-  #define TEMP_TIMER_NUM        1  // Timer Index for Temperature
+#ifndef MF_TIMER_TEMP
+  #define MF_TIMER_TEMP         1  // Timer Index for Temperature
 #endif
 
 #define TEMP_TIMER_RATE        1000000
@@ -58,12 +58,12 @@ typedef uint32_t hal_timer_t;
 #define PULSE_TIMER_PRESCALE   STEPPER_TIMER_PRESCALE
 #define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US
 
-#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(STEP_TIMER_NUM)
-#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM)
-#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM)
+#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_STEP)
+#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_STEP)
+#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(MF_TIMER_STEP)
 
-#define ENABLE_TEMPERATURE_INTERRUPT() HAL_timer_enable_interrupt(TEMP_TIMER_NUM)
-#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM)
+#define ENABLE_TEMPERATURE_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_TEMP)
+#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_TEMP)
 
 #ifndef HAL_STEP_TIMER_ISR
   #define HAL_STEP_TIMER_ISR()  extern "C" void TIMER0_IRQHandler()
@@ -77,7 +77,6 @@ typedef uint32_t hal_timer_t;
 #define HAL_PWM_TIMER_ISR()   extern "C" void TIMER3_IRQHandler()
 #define HAL_PWM_TIMER_IRQn
 
-
 void HAL_timer_init();
 void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency);
 
@@ -93,5 +92,5 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num);
 void HAL_timer_disable_interrupt(const uint8_t timer_num);
 bool HAL_timer_interrupt_enabled(const uint8_t timer_num);
 
-#define HAL_timer_isr_prologue(TIMER_NUM)
-#define HAL_timer_isr_epilogue(TIMER_NUM)
+#define HAL_timer_isr_prologue(T) NOOP
+#define HAL_timer_isr_epilogue(T) NOOP
diff --git a/Marlin/src/HAL/LPC1768/DebugMonitor.cpp b/Marlin/src/HAL/LPC1768/DebugMonitor.cpp
deleted file mode 100644
index 783b10cfac..0000000000
--- a/Marlin/src/HAL/LPC1768/DebugMonitor.cpp
+++ /dev/null
@@ -1,322 +0,0 @@
-/**
- * Marlin 3D Printer Firmware
- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
- *
- * Based on Sprinter and grbl.
- * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see .
- *
- */
-#ifdef TARGET_LPC1768
-
-#include "../../core/macros.h"
-#include "../../core/serial.h"
-#include 
-
-#include "../shared/backtrace/unwinder.h"
-#include "../shared/backtrace/unwmemaccess.h"
-#include "watchdog.h"
-#include 
-
-
-// Debug monitor that dumps to the Programming port all status when
-// an exception or WDT timeout happens - And then resets the board
-
-// All the Monitor routines must run with interrupts disabled and
-// under an ISR execution context. That is why we cannot reuse the
-// Serial interrupt routines or any C runtime, as we don't know the
-// state we are when running them
-
-// A SW memory barrier, to ensure GCC does not overoptimize loops
-#define sw_barrier() __asm__ volatile("": : :"memory");
-
-// (re)initialize UART0 as a monitor output to 250000,n,8,1
-static void TXBegin() {
-}
-
-// Send character through UART with no interrupts
-static void TX(char c) {
-  _DBC(c);
-}
-
-// Send String through UART
-static void TX(const char* s) {
-  while (*s) TX(*s++);
-}
-
-static void TXDigit(uint32_t d) {
-  if (d < 10) TX((char)(d+'0'));
-  else if (d < 16) TX((char)(d+'A'-10));
-  else TX('?');
-}
-
-// Send Hex number thru UART
-static void TXHex(uint32_t v) {
-  TX("0x");
-  for (uint8_t i = 0; i < 8; i++, v <<= 4)
-    TXDigit((v >> 28) & 0xF);
-}
-
-// Send Decimal number thru UART
-static void TXDec(uint32_t v) {
-  if (!v) {
-    TX('0');
-    return;
-  }
-
-  char nbrs[14];
-  char *p = &nbrs[0];
-  while (v != 0) {
-    *p++ = '0' + (v % 10);
-    v /= 10;
-  }
-  do {
-    p--;
-    TX(*p);
-  } while (p != &nbrs[0]);
-}
-
-// Dump a backtrace entry
-static bool UnwReportOut(void* ctx, const UnwReport* bte) {
-  int* p = (int*)ctx;
-
-  (*p)++;
-  TX('#'); TXDec(*p); TX(" : ");
-  TX(bte->name?bte->name:"unknown"); TX('@'); TXHex(bte->function);
-  TX('+'); TXDec(bte->address - bte->function);
-  TX(" PC:");TXHex(bte->address); TX('\n');
-  return true;
-}
-
-#ifdef UNW_DEBUG
-  void UnwPrintf(const char* format, ...) {
-    char dest[256];
-    va_list argptr;
-    va_start(argptr, format);
-    vsprintf(dest, format, argptr);
-    va_end(argptr);
-    TX(&dest[0]);
-  }
-#endif
-
-/* Table of function pointers for passing to the unwinder */
-static const UnwindCallbacks UnwCallbacks = {
-  UnwReportOut,
-  UnwReadW,
-  UnwReadH,
-  UnwReadB
-  #ifdef UNW_DEBUG
-   ,UnwPrintf
-  #endif
-};
-
-
-/**
- * HardFaultHandler_C:
- * This is called from the HardFault_HandlerAsm with a pointer the Fault stack
- * as the parameter. We can then read the values from the stack and place them
- * into local variables for ease of reading.
- * We then read the various Fault Status and Address Registers to help decode
- * cause of the fault.
- * The function ends with a BKPT instruction to force control back into the debugger
- */
-extern "C"
-void HardFault_HandlerC(unsigned long *sp, unsigned long lr, unsigned long cause) {
-
-  static const char* causestr[] = {
-    "NMI","Hard","Mem","Bus","Usage","Debug","WDT","RSTC"
-  };
-
-  UnwindFrame btf;
-
-  // Dump report to the Programming port (interrupts are DISABLED)
-  TXBegin();
-  TX("\n\n## Software Fault detected ##\n");
-  TX("Cause: "); TX(causestr[cause]); TX('\n');
-
-  TX("R0   : "); TXHex(((unsigned long)sp[0])); TX('\n');
-  TX("R1   : "); TXHex(((unsigned long)sp[1])); TX('\n');
-  TX("R2   : "); TXHex(((unsigned long)sp[2])); TX('\n');
-  TX("R3   : "); TXHex(((unsigned long)sp[3])); TX('\n');
-  TX("R12  : "); TXHex(((unsigned long)sp[4])); TX('\n');
-  TX("LR   : "); TXHex(((unsigned long)sp[5])); TX('\n');
-  TX("PC   : "); TXHex(((unsigned long)sp[6])); TX('\n');
-  TX("PSR  : "); TXHex(((unsigned long)sp[7])); TX('\n');
-
-  // Configurable Fault Status Register
-  // Consists of MMSR, BFSR and UFSR
-  TX("CFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED28)))); TX('\n');
-
-  // Hard Fault Status Register
-  TX("HFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED2C)))); TX('\n');
-
-  // Debug Fault Status Register
-  TX("DFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED30)))); TX('\n');
-
-  // Auxiliary Fault Status Register
-  TX("AFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED3C)))); TX('\n');
-
-  // Read the Fault Address Registers. These may not contain valid values.
-  // Check BFARVALID/MMARVALID to see if they are valid values
-  // MemManage Fault Address Register
-  TX("MMAR : "); TXHex((*((volatile unsigned long *)(0xE000ED34)))); TX('\n');
-
-  // Bus Fault Address Register
-  TX("BFAR : "); TXHex((*((volatile unsigned long *)(0xE000ED38)))); TX('\n');
-
-  TX("ExcLR: "); TXHex(lr); TX('\n');
-  TX("ExcSP: "); TXHex((unsigned long)sp); TX('\n');
-
-  btf.sp = ((unsigned long)sp) + 8*4; // The original stack pointer
-  btf.fp = btf.sp;
-  btf.lr = ((unsigned long)sp[5]);
-  btf.pc = ((unsigned long)sp[6]) | 1; // Force Thumb, as CORTEX only support it
-
-  // Perform a backtrace
-  TX("\nBacktrace:\n\n");
-  int ctr = 0;
-  UnwindStart(&btf, &UnwCallbacks, &ctr);
-
-  // Disable all NVIC interrupts
-  NVIC->ICER[0] = 0xFFFFFFFF;
-  NVIC->ICER[1] = 0xFFFFFFFF;
-
-  // Relocate VTOR table to default position
-  SCB->VTOR = 0;
-
-  // Clear cause of reset to prevent entering smoothie bootstrap
-  HAL_clear_reset_source();
-
-  // Restart watchdog
-  #if ENABLED(USE_WATCHDOG)
-    //WDT_Restart(WDT);
-    watchdog_init();
-  #endif
-
-  // Reset controller
-  NVIC_SystemReset();
-
-  // Nothing below here is compiled because NVIC_SystemReset loops forever
-
-  for (;;) { TERN_(USE_WATCHDOG, watchdog_init()); }
-}
-
-extern "C" {
-__attribute__((naked)) void NMI_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#0")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void HardFault_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#1")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void MemManage_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#2")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void BusFault_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#3")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void UsageFault_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#4")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void DebugMon_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#5")
-    A("b HardFault_HandlerC")
-  );
-}
-
-/* This is NOT an exception, it is an interrupt handler - Nevertheless, the framing is the same */
-__attribute__((naked)) void WDT_IRQHandler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#6")
-    A("b HardFault_HandlerC")
-  );
-}
-
-__attribute__((naked)) void RSTC_Handler() {
-  __asm__ __volatile__ (
-    ".syntax unified" "\n\t"
-    A("tst lr, #4")
-    A("ite eq")
-    A("mrseq r0, msp")
-    A("mrsne r0, psp")
-    A("mov r1,lr")
-    A("mov r2,#7")
-    A("b HardFault_HandlerC")
-  );
-}
-}
-#endif // TARGET_LPC1768
diff --git a/Marlin/src/HAL/LPC1768/HAL.cpp b/Marlin/src/HAL/LPC1768/HAL.cpp
index 939f1e8a94..9ff3a6ba59 100644
--- a/Marlin/src/HAL/LPC1768/HAL.cpp
+++ b/Marlin/src/HAL/LPC1768/HAL.cpp
@@ -25,26 +25,17 @@
 #include "../shared/Delay.h"
 #include "../../../gcode/parser.h"
 
-#if ENABLED(USE_WATCHDOG)
-  #include "watchdog.h"
-#endif
+DefaultSerial1 USBSerial(false, UsbSerial);
 
-uint32_t HAL_adc_reading = 0;
+uint32_t MarlinHAL::adc_result = 0;
 
 // U8glib required functions
-extern "C" void u8g_xMicroDelay(uint16_t val) {
-  DELAY_US(val);
+extern "C" {
+  void u8g_xMicroDelay(uint16_t val) { DELAY_US(val); }
+  void u8g_MicroDelay()              { u8g_xMicroDelay(1); }
+  void u8g_10MicroDelay()            { u8g_xMicroDelay(10); }
+  void u8g_Delay(uint16_t val)       { delay(val); }
 }
-extern "C" void u8g_MicroDelay() {
-  u8g_xMicroDelay(1);
-}
-extern "C" void u8g_10MicroDelay() {
-  u8g_xMicroDelay(10);
-}
-extern "C" void u8g_Delay(uint16_t val) {
-  delay(val);
-}
-//************************//
 
 // return free heap space
 int freeMemory() {
@@ -57,7 +48,71 @@ int freeMemory() {
   return result;
 }
 
-// scan command line for code
+void MarlinHAL::reboot() { NVIC_SystemReset(); }
+
+uint8_t MarlinHAL::get_reset_source() {
+  #if ENABLED(USE_WATCHDOG)
+    if (watchdog_timed_out()) return RST_WATCHDOG;
+  #endif
+  return RST_POWER_ON;
+}
+
+void MarlinHAL::clear_reset_source() { watchdog_clear_timeout_flag(); }
+
+void flashFirmware(const int16_t) {
+  delay(500);          // Give OS time to disconnect
+  USB_Connect(false);  // USB clear connection
+  delay(1000);         // Give OS time to notice
+  hal.reboot();
+}
+
+#if ENABLED(USE_WATCHDOG)
+
+  #include 
+
+  #define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout
+
+  void MarlinHAL::watchdog_init() {
+    #if ENABLED(WATCHDOG_RESET_MANUAL)
+      // We enable the watchdog timer, but only for the interrupt.
+
+      // Configure WDT to only trigger an interrupt
+      // Disable WDT interrupt (just in case, to avoid triggering it!)
+      NVIC_DisableIRQ(WDT_IRQn);
+
+      // We NEED memory barriers to ensure Interrupts are actually disabled!
+      // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
+      __DSB();
+      __ISB();
+
+      // Configure WDT to only trigger an interrupt
+      // Initialize WDT with the given parameters
+      WDT_Init(WDT_CLKSRC_IRC, WDT_MODE_INT_ONLY);
+
+      // Configure and enable WDT interrupt.
+      NVIC_ClearPendingIRQ(WDT_IRQn);
+      NVIC_SetPriority(WDT_IRQn, 0); // Use highest priority, so we detect all kinds of lockups
+      NVIC_EnableIRQ(WDT_IRQn);
+    #else
+      WDT_Init(WDT_CLKSRC_IRC, WDT_MODE_RESET);
+    #endif
+    WDT_Start(WDT_TIMEOUT_US);
+  }
+
+  void MarlinHAL::watchdog_refresh() {
+    WDT_Feed();
+    #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED)
+      TOGGLE(LED_PIN);  // heartbeat indicator
+    #endif
+  }
+
+  // Timeout state
+  bool MarlinHAL::watchdog_timed_out() { return TEST(WDT_ReadTimeOutFlag(), 0); }
+  void MarlinHAL::watchdog_clear_timeout_flag() { WDT_ClrTimeOutFlag(); }
+
+#endif // USE_WATCHDOG
+
+// For M42/M43, scan command line for pin code
 //   return index into pin map array if found and the pin is valid.
 //   return dval if not found or not a valid pin.
 int16_t PARSED_PIN_INDEX(const char code, const int16_t dval) {
@@ -66,17 +121,4 @@ int16_t PARSED_PIN_INDEX(const char code, const int16_t dval) {
   return ind > -1 ? ind : dval;
 }
 
-void flashFirmware(const int16_t) { NVIC_SystemReset(); }
-
-void HAL_clear_reset_source(void) {
-  TERN_(USE_WATCHDOG, watchdog_clear_timeout_flag());
-}
-
-uint8_t HAL_get_reset_source(void) {
-  #if ENABLED(USE_WATCHDOG)
-    if (watchdog_timed_out()) return RST_WATCHDOG;
-  #endif
-  return RST_POWER_ON;
-}
-
 #endif // TARGET_LPC1768
diff --git a/Marlin/src/HAL/LPC1768/HAL.h b/Marlin/src/HAL/LPC1768/HAL.h
index cb637e715d..b0eeb983b4 100644
--- a/Marlin/src/HAL/LPC1768/HAL.h
+++ b/Marlin/src/HAL/LPC1768/HAL.h
@@ -28,8 +28,6 @@
 
 #define CPU_32_BIT
 
-void HAL_init();
-
 #include 
 #include 
 #include 
@@ -40,80 +38,82 @@ extern "C" volatile uint32_t _millis;
 #include "../shared/math_32bit.h"
 #include "../shared/HAL_SPI.h"
 #include "fastio.h"
-#include "watchdog.h"
 #include "MarlinSerial.h"
 
 #include 
 #include 
 #include 
 
-// i2c uses 8-bit shifted address
-#define I2C_ADDRESS(A) uint8_t((A) << 1)
+// ------------------------
+// Serial ports
+// ------------------------
 
-//
-// Default graphical display delays
-//
-#ifndef ST7920_DELAY_1
-  #define ST7920_DELAY_1 DELAY_NS(600)
-#endif
-#ifndef ST7920_DELAY_2
-  #define ST7920_DELAY_2 DELAY_NS(750)
-#endif
-#ifndef ST7920_DELAY_3
-  #define ST7920_DELAY_3 DELAY_NS(750)
-#endif
+typedef ForwardSerial1Class< decltype(UsbSerial) > DefaultSerial1;
+extern DefaultSerial1 USBSerial;
 
 #define _MSERIAL(X) MSerial##X
 #define MSERIAL(X) _MSERIAL(X)
-#define MSerial0 MSerial
 
 #if SERIAL_PORT == -1
-  #define MYSERIAL0 UsbSerial
+  #define MYSERIAL1 USBSerial
 #elif WITHIN(SERIAL_PORT, 0, 3)
-  #define MYSERIAL0 MSERIAL(SERIAL_PORT)
+  #define MYSERIAL1 MSERIAL(SERIAL_PORT)
 #else
-  #error "SERIAL_PORT must be from -1 to 3. Please update your configuration."
+  #error "SERIAL_PORT must be from 0 to 3. You can also use -1 if the board supports Native USB."
 #endif
 
 #ifdef SERIAL_PORT_2
   #if SERIAL_PORT_2 == -1
-    #define MYSERIAL1 UsbSerial
+    #define MYSERIAL2 USBSerial
   #elif WITHIN(SERIAL_PORT_2, 0, 3)
-    #define MYSERIAL1 MSERIAL(SERIAL_PORT_2)
+    #define MYSERIAL2 MSERIAL(SERIAL_PORT_2)
   #else
-    #error "SERIAL_PORT_2 must be from -1 to 3. Please update your configuration."
+    #error "SERIAL_PORT_2 must be from 0 to 3. You can also use -1 if the board supports Native USB."
+  #endif
+#endif
+
+#ifdef SERIAL_PORT_3
+  #if SERIAL_PORT_3 == -1
+    #define MYSERIAL3 USBSerial
+  #elif WITHIN(SERIAL_PORT_3, 0, 3)
+    #define MYSERIAL3 MSERIAL(SERIAL_PORT_3)
+  #else
+    #error "SERIAL_PORT_3 must be from 0 to 3. You can also use -1 if the board supports Native USB."
+  #endif
+#endif
+
+#ifdef MMU2_SERIAL_PORT
+  #if MMU2_SERIAL_PORT == -1
+    #define MMU2_SERIAL USBSerial
+  #elif WITHIN(MMU2_SERIAL_PORT, 0, 3)
+    #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT)
+  #else
+    #error "MMU2_SERIAL_PORT must be from 0 to 3. You can also use -1 if the board supports Native USB."
   #endif
 #endif
 
 #ifdef LCD_SERIAL_PORT
   #if LCD_SERIAL_PORT == -1
-    #define LCD_SERIAL UsbSerial
+    #define LCD_SERIAL USBSerial
   #elif WITHIN(LCD_SERIAL_PORT, 0, 3)
     #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT)
   #else
-    #error "LCD_SERIAL_PORT must be from -1 to 3. Please update your configuration."
+    #error "LCD_SERIAL_PORT must be from 0 to 3. You can also use -1 if the board supports Native USB."
+  #endif
+  #if HAS_DGUS_LCD
+    #define SERIAL_GET_TX_BUFFER_FREE() LCD_SERIAL.available()
   #endif
 #endif
 
 //
 // Interrupts
 //
-#define CRITICAL_SECTION_START()  uint32_t primask = __get_PRIMASK(); __disable_irq()
-#define CRITICAL_SECTION_END()    if (!primask) __enable_irq()
-#define ISRS_ENABLED() (!__get_PRIMASK())
-#define ENABLE_ISRS()  __enable_irq()
-#define DISABLE_ISRS() __disable_irq()
+
+#define CRITICAL_SECTION_START()  const bool irqon = !__get_PRIMASK(); __disable_irq()
+#define CRITICAL_SECTION_END()    if (irqon) __enable_irq()
 
 //
-// Utility functions
-//
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-function"
-int freeMemory();
-#pragma GCC diagnostic pop
-
-//
-// ADC API
+// ADC
 //
 
 #define ADC_MEDIAN_FILTER_SIZE (23) // Higher values increase step delay (phase shift),
@@ -132,20 +132,9 @@ int freeMemory();
 #define HAL_ADC_RESOLUTION     12   // 15 bit maximum, raw temperature is stored as int16_t
 #define HAL_ADC_FILTERED            // Disable oversampling done in Marlin as ADC values already filtered in HAL
 
-using FilteredADC = LPC176x::ADC;
-extern uint32_t HAL_adc_reading;
-[[gnu::always_inline]] inline void HAL_start_adc(const pin_t pin) {
-  HAL_adc_reading = FilteredADC::read(pin) >> (16 - HAL_ADC_RESOLUTION); // returns 16bit value, reduce to required bits
-}
-[[gnu::always_inline]] inline uint16_t HAL_read_adc() {
-  return HAL_adc_reading;
-}
-
-#define HAL_adc_init()
-#define HAL_ANALOG_SELECT(pin) FilteredADC::enable_channel(pin)
-#define HAL_START_ADC(pin)     HAL_start_adc(pin)
-#define HAL_READ_ADC()         HAL_read_adc()
-#define HAL_ADC_READY()        (true)
+//
+// Pin Mapping for M42, M43, M226
+//
 
 // Test whether the pin is valid
 constexpr bool VALID_PIN(const pin_t pin) {
@@ -170,39 +159,109 @@ constexpr pin_t GET_PIN_MAP_PIN(const int16_t index) {
 // Parse a G-code word into a pin index
 int16_t PARSED_PIN_INDEX(const char code, const int16_t dval);
 // P0.6 thru P0.9 are for the onboard SD card
-#define HAL_SENSITIVE_PINS P0_06, P0_07, P0_08, P0_09
+#define HAL_SENSITIVE_PINS P0_06, P0_07, P0_08, P0_09,
 
-#define HAL_IDLETASK 1
-void HAL_idletask();
+// ------------------------
+// Defines
+// ------------------------
 
 #define PLATFORM_M997_SUPPORT
 void flashFirmware(const int16_t);
 
 #define HAL_CAN_SET_PWM_FREQ   // This HAL supports PWM Frequency adjustment
 
-/**
- * set_pwm_frequency
- *  Set the frequency of the timer corresponding to the provided pin
- *  All Hardware PWM pins run at the same frequency and all
- *  Software PWM pins run at the same frequency
- */
-void set_pwm_frequency(const pin_t pin, int f_desired);
+// Default graphical display delays
+#define CPU_ST7920_DELAY_1 600
+#define CPU_ST7920_DELAY_2 750
+#define CPU_ST7920_DELAY_3 750
 
-/**
- * set_pwm_duty
- *  Set the PWM duty cycle of the provided pin to the provided value
- *  Optionally allows inverting the duty cycle [default = false]
- *  Optionally allows changing the maximum size of the provided value to enable finer PWM duty control [default = 255]
- */
-void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);
+// ------------------------
+// Free Memory Accessor
+// ------------------------
 
-// Reset source
-void HAL_clear_reset_source(void);
-uint8_t HAL_get_reset_source(void);
-
-inline void HAL_reboot() {}  // reboot the board or restart the bootloader
-
-// Add strcmp_P if missing
-#ifndef strcmp_P
-  #define strcmp_P(a, b) strcmp((a), (b))
+#pragma GCC diagnostic push
+#if GCC_VERSION <= 50000
+  #pragma GCC diagnostic ignored "-Wunused-function"
 #endif
+
+int freeMemory();
+
+#pragma GCC diagnostic pop
+
+// ------------------------
+// MarlinHAL Class
+// ------------------------
+
+class MarlinHAL {
+public:
+
+  // Earliest possible init, before setup()
+  MarlinHAL() {}
+
+  static void init();          // Called early in setup()
+  static void init_board() {}  // Called less early in setup()
+  static void reboot();        // Restart the firmware from 0x0
+
+  // Interrupts
+  static bool isr_state() { return !__get_PRIMASK(); }
+  static void isr_on()  { __enable_irq(); }
+  static void isr_off() { __disable_irq(); }
+
+  static void delay_ms(const int ms) { _delay_ms(ms); }
+
+  // Watchdog
+  static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {});
+  static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {});
+  static bool watchdog_timed_out() IF_DISABLED(USE_WATCHDOG, { return false; });
+  static void watchdog_clear_timeout_flag() IF_DISABLED(USE_WATCHDOG, {});
+
+  // Tasks, called from idle()
+  static void idletask();
+
+  // Reset
+  static uint8_t get_reset_source();
+  static void clear_reset_source();
+
+  // Free SRAM
+  static int freeMemory() { return ::freeMemory(); }
+
+  //
+  // ADC Methods
+  //
+
+  using FilteredADC = LPC176x::ADC;
+
+  // Called by Temperature::init once at startup
+  static void adc_init() {}
+
+  // Called by Temperature::init for each sensor at startup
+  static void adc_enable(const pin_t pin) {
+    FilteredADC::enable_channel(pin);
+  }
+
+  // Begin ADC sampling on the given pin. Called from Temperature::isr!
+  static uint32_t adc_result;
+  static void adc_start(const pin_t pin) {
+    adc_result = FilteredADC::read(pin) >> (16 - HAL_ADC_RESOLUTION); // returns 16bit value, reduce to required bits
+  }
+
+  // Is the ADC ready for reading?
+  static bool adc_ready() { return true; }
+
+  // The current value of the ADC register
+  static uint16_t adc_value() { return uint16_t(adc_result); }
+
+  /**
+   * Set the PWM duty cycle for the pin to the given value.
+   * Optionally invert the duty cycle [default = false]
+   * Optionally change the scale of the provided value to enable finer PWM duty control [default = 255]
+   */
+  static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);
+
+  /**
+   * Set the frequency of the timer corresponding to the provided pin
+   * All Hardware PWM pins will run at the same frequency and
+   * All Software PWM pins will run at the same frequency
+   */
+  static void set_pwm_frequency(const pin_t pin, const uint16_t f_desired);
+};
diff --git a/Marlin/src/HAL/LPC1768/HAL_SPI.cpp b/Marlin/src/HAL/LPC1768/HAL_SPI.cpp
index b3d2908ac9..257d8579e2 100644
--- a/Marlin/src/HAL/LPC1768/HAL_SPI.cpp
+++ b/Marlin/src/HAL/LPC1768/HAL_SPI.cpp
@@ -55,27 +55,29 @@
 #include