diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 54627d462b..77479946b5 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -38,7 +38,7 @@
"platformio.platformio-ide",
"marlinfirmware.auto-build",
"editorconfig.editorconfig"
- ],
+ ]
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
diff --git a/.github/workflows/ci-validate-boards.yml b/.github/workflows/ci-validate-boards.yml
index 36f3a3522a..aa6d284f06 100644
--- a/.github/workflows/ci-validate-boards.yml
+++ b/.github/workflows/ci-validate-boards.yml
@@ -9,14 +9,14 @@ name: CI - Validate boards.h
on:
pull_request:
branches:
- - bugfix-2.1.x
+ - bugfix-2.1.x
paths:
- - 'Marlin/src/core/boards.h'
+ - "Marlin/src/core/boards.h"
push:
branches:
- - bugfix-2.1.x
+ - bugfix-2.1.x
paths:
- - 'Marlin/src/core/boards.h'
+ - "Marlin/src/core/boards.h"
jobs:
validate_pins_files:
@@ -26,23 +26,23 @@ jobs:
runs-on: ubuntu-22.04
steps:
- - name: Check out the PR
- uses: actions/checkout@v4
+ - name: Check out the PR
+ uses: actions/checkout@v4
- - name: Cache pip
- uses: actions/cache@v4
- with:
- path: ~/.cache/pip
- key: ${{ runner.os }}-pip-boards-v1
- restore-keys: |
- ${{ runner.os }}-pip-boards-
+ - name: Cache pip
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-validation-v1
+ restore-keys: |
+ ${{ runner.os }}-pip-validation-
- - name: Select Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: '3.9'
- architecture: 'x64'
+ - name: Select Python 3.9
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.9"
+ architecture: "x64"
- - name: Validate core/boards.h
- run: |
- make validate-boards -j
+ - name: Validate core/boards.h
+ run: |
+ make validate-boards -j
diff --git a/.github/workflows/ci-validate-lines.yml b/.github/workflows/ci-validate-lines.yml
new file mode 100644
index 0000000000..367db12e6c
--- /dev/null
+++ b/.github/workflows/ci-validate-lines.yml
@@ -0,0 +1,40 @@
+#
+# ci-validate-lines.yml
+# Validate that all text files are unchanged by linesformat.py
+#
+
+name: CI - Validate Source Files
+
+on:
+ pull_request:
+ branches:
+ - bugfix-2.1.x
+ - 2.1.x
+ push:
+ branches:
+ - bugfix-2.1.x
+ - 2.1.x
+
+jobs:
+ validate_source_files:
+ name: Validate Source Files
+ if: github.repository == 'MarlinFirmware/Marlin'
+
+ runs-on: ubuntu-22.04
+
+ steps:
+ - name: Check out the PR
+ uses: actions/checkout@v4
+
+ - name: Cache node_modules
+ uses: actions/cache@v4
+ with:
+ path: node_modules
+ key: ${{ runner.os }}-npm-lines-v1
+ restore-keys: |
+ ${{ runner.os }}-npm-lines-
+
+ - name: Validate text file formatting
+ run: |
+ npm install --save-dev prettier
+ make validate-lines -j
diff --git a/.github/workflows/ci-validate-pins.yml b/.github/workflows/ci-validate-pins.yml
index ae777427e7..2086fea37a 100644
--- a/.github/workflows/ci-validate-pins.yml
+++ b/.github/workflows/ci-validate-pins.yml
@@ -8,18 +8,18 @@ name: CI - Validate Pins Files
on:
pull_request:
branches:
- - bugfix-2.1.x
+ - bugfix-2.1.x
# Cannot be enabled on 2.1.x until it contains the unit test framework
#- 2.1.x
paths:
- - 'Marlin/src/pins/*/**'
+ - "Marlin/src/pins/*/**"
push:
branches:
- - bugfix-2.1.x
+ - bugfix-2.1.x
# Cannot be enabled on 2.1.x until it contains the unit test framework
#- 2.1.x
paths:
- - 'Marlin/src/pins/*/**'
+ - "Marlin/src/pins/*/**"
jobs:
validate_pins_files:
@@ -29,23 +29,23 @@ jobs:
runs-on: ubuntu-22.04
steps:
- - name: Check out the PR
- uses: actions/checkout@v4
+ - name: Check out the PR
+ uses: actions/checkout@v4
- - name: Cache pip
- uses: actions/cache@v4
- with:
- path: ~/.cache/pip
- key: ${{ runner.os }}-pip-pins-v1
- restore-keys: |
- ${{ runner.os }}-pip-pins-
+ - name: Cache pip
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-validation-v1
+ restore-keys: |
+ ${{ runner.os }}-pip-validation-
- - name: Select Python 3.9
- uses: actions/setup-python@v5
- with:
- python-version: '3.9'
- architecture: 'x64'
+ - name: Select Python 3.9
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.9"
+ architecture: "x64"
- - name: Validate all pins files
- run: |
- make validate-pins -j
+ - name: Validate all pins files
+ run: |
+ make validate-pins -j
diff --git a/.gitignore b/.gitignore
index daa5c5c288..87d7ef47d3 100755
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,11 @@ out-language/
*.gen
*.sublime-workspace
+# npm
+node_modules/
+package.json
+package-lock.json
+
# OS
applet/
.DS_Store
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000000..62761b99e9
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,10 @@
+# Prettier Ignore file
+*.min.js
+web-ui/
+buildroot/share/PlatformIO/boards
+buildroot/share/PlatformIO/variants
+*.sublime-project
+*.sublime-syntax
+.github
+.vscode
+launch.json
diff --git a/Makefile b/Makefile
index 23826497b6..f1f64e1342 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,7 @@ help:
@echo "Tasks for local development:"
@echo "make marlin : Build Marlin for the configured board"
@echo "make format-pins -j : Reformat all pins files (-j for parallel execution)"
+ @echo "make validate-lines -j : Validate line endings, fails on trailing whitespace, etc."
@echo "make validate-pins -j : Validate all pins files, fails if any require reformatting"
@echo "make validate-boards -j : Validate boards.h and pins.h for standards compliance"
@echo "make tests-single-ci : Run a single test from inside the CI"
@@ -95,7 +96,7 @@ PINS := $(shell find Marlin/src/pins -mindepth 2 -name '*.h')
.PHONY: $(PINS) format-pins validate-pins
$(PINS): %:
- @echo "Formatting $@"
+ @echo "Formatting pins $@"
@python $(SCRIPTS_DIR)/pinsformat.py $< $@
format-pins: $(PINS)
@@ -104,6 +105,17 @@ validate-pins: format-pins
@echo "Validating pins files"
@git diff --exit-code || (git status && echo "\nError: Pins files are not formatted correctly. Run \"make format-pins\" to fix.\n" && exit 1)
+.PHONY: format-lines validate-lines
+
+format-lines:
+ @echo "Formatting all sources"
+ @python $(SCRIPTS_DIR)/linesformat.py buildroot
+ @python $(SCRIPTS_DIR)/linesformat.py Marlin
+
+validate-lines:
+ @echo "Validating text formatting"
+ @npx prettier --check . --editorconfig --object-wrap preserve
+
BOARDS_FILE := Marlin/src/core/boards.h
.PHONY: validate-boards
diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h
index bcf9098bc6..a9d26ac920 100644
--- a/Marlin/Configuration_adv.h
+++ b/Marlin/Configuration_adv.h
@@ -3517,6 +3517,7 @@
//#define W_STALL_SENSITIVITY 8
//#define SPI_ENDSTOPS // TMC2130, TMC2240, and TMC5160
//#define IMPROVE_HOMING_RELIABILITY
+ //#define SENSORLESS_STALLGUARD_DELAY 0 // (ms) Delay to allow drivers to settle
#endif
// @section tmc/config
@@ -4004,7 +4005,7 @@
#endif
/**
- * M115 - Report capabilites. Disable to save ~1150 bytes of flash.
+ * M115 - Report capabilities. Disable to save ~1150 bytes of flash.
* Some hosts (and serial TFT displays) rely on this feature.
*/
#define CAPABILITIES_REPORT
diff --git a/Marlin/Version.h b/Marlin/Version.h
index a86ee5f5f4..75a1397d0d 100644
--- a/Marlin/Version.h
+++ b/Marlin/Version.h
@@ -41,7 +41,7 @@
* here we define this default string as the date where the latest release
* version was tagged.
*/
-//#define STRING_DISTRIBUTION_DATE "2025-08-26"
+//#define STRING_DISTRIBUTION_DATE "2025-09-09"
/**
* The protocol for communication to the host. Protocol indicates communication
diff --git a/Marlin/src/HAL/DUE/usb/uotghs_device_due.c b/Marlin/src/HAL/DUE/usb/uotghs_device_due.c
index 01dda7e7fe..5635f2ba0c 100644
--- a/Marlin/src/HAL/DUE/usb/uotghs_device_due.c
+++ b/Marlin/src/HAL/DUE/usb/uotghs_device_due.c
@@ -523,7 +523,7 @@ static bool udd_ep_interrupt(void);
* \internal
* \brief Function called by UOTGHS interrupt to manage USB Device interrupts
*
- * USB Device interrupt events are splited in three parts:
+ * USB Device interrupt events are split in three parts:
* - USB line events (SOF, reset, suspend, resume, wakeup)
* - control endpoint events (setup reception, end of data transfer, underflow, overflow, stall)
* - bulk/interrupt/isochronous endpoints events (end of data transfer)
@@ -1567,7 +1567,7 @@ static void udd_ctrl_out_received(void)
udd_ctrl_payload_buf_cnt))) {
// End of reception because it is a short packet
// Before send ZLP, call intermediate callback
- // in case of data receiv generate a stall
+ // in case of data receive generate a stall
udd_g_ctrlreq.payload_size = udd_ctrl_payload_buf_cnt;
if (NULL != udd_g_ctrlreq.over_under_run) {
if (!udd_g_ctrlreq.over_under_run()) {
@@ -1808,7 +1808,7 @@ static void udd_ep_trans_done(udd_ep_id_t ep)
}
if (ptr_job->buf_cnt != ptr_job->buf_size) {
- // Need to send or receiv other data
+ // Need to send or receive other data
next_trans = ptr_job->buf_size - ptr_job->buf_cnt;
if (UDD_ENDPOINT_MAX_TRANS < next_trans) {
diff --git a/Marlin/src/HAL/GD32_MFL/README.md b/Marlin/src/HAL/GD32_MFL/README.md
index af23a37f2f..61800eda1c 100644
--- a/Marlin/src/HAL/GD32_MFL/README.md
+++ b/Marlin/src/HAL/GD32_MFL/README.md
@@ -3,6 +3,7 @@
This HAL is eventually intended to act as the generic HAL for all GD32 chips using the MFL library.
Currently it supports:
- * GD32F303RET6
+
+- GD32F303RET6
Targeting the official [MFL Arduino Core](https://github.com/bnmguy/ArduinoCore_MFL).
diff --git a/Marlin/src/HAL/GD32_MFL/sd/SDCard.h b/Marlin/src/HAL/GD32_MFL/sd/SDCard.h
index b03d128dc8..de28c40809 100644
--- a/Marlin/src/HAL/GD32_MFL/sd/SDCard.h
+++ b/Marlin/src/HAL/GD32_MFL/sd/SDCard.h
@@ -73,7 +73,7 @@ public:
// Interrupt handler
void handle_interrupts();
- // Varaible stored parameters
+ // Variable stored parameters
auto get_scr(uint16_t rca, uint32_t* scr) -> SDIO_Error_Type;
auto store_cid() -> SDIO_Error_Type;
auto store_csd() -> SDIO_Error_Type;
diff --git a/Marlin/src/HAL/HC32/timers.h b/Marlin/src/HAL/HC32/timers.h
index c0014df604..e5ab3f21f5 100644
--- a/Marlin/src/HAL/HC32/timers.h
+++ b/Marlin/src/HAL/HC32/timers.h
@@ -49,7 +49,7 @@ extern Timer0 step_timer;
* See https://github.com/MarlinFirmware/Marlin/pull/27099 for more information.
*
* NOTE: If the 'constexpr' requirement is ever lifted, TIMER0_BASE_FREQUENCY could
- * be used instead. Tho this would probably not make any noticable difference.
+ * be used instead. Tho this would probably not make any noticeable difference.
*/
#define HAL_TIMER_RATE F_PCLK1
diff --git a/Marlin/src/HAL/RP2040/HAL.cpp b/Marlin/src/HAL/RP2040/HAL.cpp
index f0d9e4eec6..1b519e4004 100644
--- a/Marlin/src/HAL/RP2040/HAL.cpp
+++ b/Marlin/src/HAL/RP2040/HAL.cpp
@@ -112,7 +112,7 @@ void MarlinHAL::reboot() { watchdog_reboot(0, 0, 1); }
void MarlinHAL::watchdog_init() {
#if DISABLED(DISABLE_WATCHDOG_INIT)
- static_assert(WDT_TIMEOUT_US > 1000, "WDT Timout is too small, aborting");
+ static_assert(WDT_TIMEOUT_US > 1000, "WDT Timeout is too small, aborting");
watchdog_enable(WDT_TIMEOUT_US/1000, true);
#endif
}
diff --git a/Marlin/src/HAL/SAMD21/timers.cpp b/Marlin/src/HAL/SAMD21/timers.cpp
index b5f1d4f7bd..4ec6e5d867 100644
--- a/Marlin/src/HAL/SAMD21/timers.cpp
+++ b/Marlin/src/HAL/SAMD21/timers.cpp
@@ -176,7 +176,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
tc->COUNT32.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
//set prescaler
//the clock normally counts at the GCLK_TC frequency, but we can set it to divide that frequency to slow it down
- //you can use different prescaler divisons here like TC_CTRLA_PRESCALER_DIV1 to get a different range
+ //you can use different prescaler divisions here like TC_CTRLA_PRESCALER_DIV1 to get a different range
tc->COUNT32.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1 | TC_CTRLA_ENABLE; //it will divide GCLK_TC frequency by 1024
//set the compare-capture register.
//The counter will count up to this value (it's a 16bit counter so we use uint16_t)
diff --git a/Marlin/src/HAL/STM32/HardwareSerial.cpp b/Marlin/src/HAL/STM32/HardwareSerial.cpp
index d9948d0a34..58360cc31e 100644
--- a/Marlin/src/HAL/STM32/HardwareSerial.cpp
+++ b/Marlin/src/HAL/STM32/HardwareSerial.cpp
@@ -209,7 +209,7 @@ HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) {
}
#endif
- else { // else get the pins of the first peripheral occurence in PinMap
+ else { // else get the pins of the first peripheral occurrence in PinMap
_serial.pin_rx = pinmap_pin(peripheral, PinMap_UART_RX);
_serial.pin_tx = pinmap_pin(peripheral, PinMap_UART_TX);
}
diff --git a/Marlin/src/HAL/STM32/README.md b/Marlin/src/HAL/STM32/README.md
index 7680df6654..cf8aa50d50 100644
--- a/Marlin/src/HAL/STM32/README.md
+++ b/Marlin/src/HAL/STM32/README.md
@@ -3,9 +3,10 @@
This HAL is intended to act as the generic STM32 HAL for all STM32 chips (The whole F, H and L family).
Currently it supports:
- * STM32F0xx
- * STM32F1xx
- * STM32F4xx
- * STM32F7xx
+
+- STM32F0xx
+- STM32F1xx
+- STM32F4xx
+- STM32F7xx
Targeting the official [Arduino STM32 Core](https://github.com/stm32duino/Arduino_Core_STM32).
diff --git a/Marlin/src/HAL/STM32F1/HAL.cpp b/Marlin/src/HAL/STM32F1/HAL.cpp
index 70d3414135..fc1680cbf4 100644
--- a/Marlin/src/HAL/STM32F1/HAL.cpp
+++ b/Marlin/src/HAL/STM32F1/HAL.cpp
@@ -131,7 +131,7 @@ uint16_t MarlinHAL::adc_result;
#include
-// Init the AD in continuous capture mode
+// Init the ADC in continuous capture mode
void MarlinHAL::adc_init() {
static const uint8_t adc_pins[] = {
OPTITEM(HAS_TEMP_ADC_0, TEMP_0_PIN )
diff --git a/Marlin/src/HAL/STM32F1/README.md b/Marlin/src/HAL/STM32F1/README.md
index b5bd5141fb..e289f35433 100644
--- a/Marlin/src/HAL/STM32F1/README.md
+++ b/Marlin/src/HAL/STM32F1/README.md
@@ -5,6 +5,7 @@ This HAL is for STM32F103 boards used with [Arduino STM32](https://github.com/ro
Currently has been tested in Malyan M200 (103CBT6), SKRmini (103RCT6), Chitu 3d (103ZET6), and various 103VET6 boards.
### Main developers:
+
- Victorpv
- xC000005
- thisiskeithb
diff --git a/Marlin/src/HAL/shared/backtrace/unwarm.h b/Marlin/src/HAL/shared/backtrace/unwarm.h
index 72ea0b0627..8e432ebe77 100644
--- a/Marlin/src/HAL/shared/backtrace/unwarm.h
+++ b/Marlin/src/HAL/shared/backtrace/unwarm.h
@@ -15,7 +15,7 @@
#include "unwinder.h"
-/** The maximum number of instructions to interpet in a function.
+/** The maximum number of instructions to interpret in a function.
* Unwinding will be unconditionally stopped and UNWIND_EXHAUSTED returned
* if more than this number of instructions are interpreted in a single
* function without unwinding a stack frame. This prevents infinite loops
diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp
index ae10972f5b..175fd0c315 100644
--- a/Marlin/src/MarlinCore.cpp
+++ b/Marlin/src/MarlinCore.cpp
@@ -483,7 +483,7 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) {
// Check if the kill button was pressed and wait to ensure the signal is not noise
// typically caused by poor insulation and grounding on LCD cables.
// Lower numbers here will increase response time and therefore safety rating.
- // It is recommended to set this as low as possibe without false triggers.
+ // It is recommended to set this as low as possible without false triggers.
// -------------------------------------------------------------------------------
#ifndef KILL_DELAY
#define KILL_DELAY 250
diff --git a/Marlin/src/feature/easythreed_ui.cpp b/Marlin/src/feature/easythreed_ui.cpp
index 8974c5befd..bfa51096f5 100644
--- a/Marlin/src/feature/easythreed_ui.cpp
+++ b/Marlin/src/feature/easythreed_ui.cpp
@@ -131,7 +131,7 @@ void EasythreedUI::loadButton() {
break;
case FS_PROCEED: {
- // Feed or Retract just once. Hard abort all moves and return to idle on swicth release.
+ // Feed or Retract just once. Hard abort all moves and return to idle on switch release.
static bool flag = false;
if (READ(BTN_RETRACT) && READ(BTN_FEED)) { // Switch in center position (stop)
flag = false; // Restore flag to false
diff --git a/Marlin/src/feature/mmu/mmu2-serial-protocol.md b/Marlin/src/feature/mmu/mmu2-serial-protocol.md
index 088d41b446..40799d41a9 100644
--- a/Marlin/src/feature/mmu/mmu2-serial-protocol.md
+++ b/Marlin/src/feature/mmu/mmu2-serial-protocol.md
@@ -1,5 +1,4 @@
-Startup sequence
-================
+# Startup sequence
When initialized, MMU sends
@@ -20,18 +19,17 @@ We follow with
#endif
- MMU <= 'P0\n'
-- MMU => '*FINDA status*\n'
+- MMU => '_FINDA status_\n'
Now we are sure MMU is available and ready. If there was a timeout or other communication problem somewhere, printer will be killed.
-- *Firmware version* is an integer value, but we don't care about it
-- *Build number* is an integer value and has to be >=126, or =>132 if 12V mode is enabled
-- *FINDA status* is 1 if the filament is loaded to the extruder, 0 otherwise
+- _Firmware version_ is an integer value, but we don't care about it
+- _Build number_ is an integer value and has to be >=126, or =>132 if 12V mode is enabled
+- _FINDA status_ is 1 if the filament is loaded to the extruder, 0 otherwise
-*Build number* is checked against the required value, if it does not match, printer is halted.
+_Build number_ is checked against the required value, if it does not match, printer is halted.
-Toolchange
-==========
+# Toolchange
- MMU <= 'T*Filament index*\n'
@@ -51,16 +49,14 @@ When done, the MMU sends
We don't wait for a response here but immediately continue with the next G-code which should
be one or more extruder moves to feed the filament into the hotend.
-FINDA status
-============
+# FINDA status
- MMU <= 'P0\n'
-- MMU => '*FINDA status*\n'
+- MMU => '_FINDA status_\n'
-*FINDA status* is 1 if the is filament loaded to the extruder, 0 otherwise. This could be used as filament runout sensor if probed regularly.
+_FINDA status_ is 1 if the is filament loaded to the extruder, 0 otherwise. This could be used as filament runout sensor if probed regularly.
-Load filament
-=============
+# Load filament
- MMU <= 'L*Filament index*\n'
@@ -68,8 +64,7 @@ MMU will feed filament down to the extruder, when done
- MMU => 'ok\n'
-Unload filament
-=============
+# Unload filament
- MMU <= 'U0\n'
@@ -77,8 +72,7 @@ MMU will retract current filament from the extruder, when done
- MMU => 'ok\n'
-Eject filament
-==============
+# Eject filament
- MMU <= 'E*Filament index*\n'
- MMU => 'ok\n'
diff --git a/Marlin/src/feature/mmu3/mmu3-serial-protocol.md b/Marlin/src/feature/mmu3/mmu3-serial-protocol.md
index fd0f1fbcba..0d0f7ac6e0 100644
--- a/Marlin/src/feature/mmu3/mmu3-serial-protocol.md
+++ b/Marlin/src/feature/mmu3/mmu3-serial-protocol.md
@@ -28,6 +28,7 @@ This set of responses combines to indicate firmware version 3.0.2.
## Startup sequence
When initialized the MMU waits for requests. Marlin repeatedly sends `S0` commands until it gets an answer:
+
```
MMU3:>S0*c6\n
MMU3:>S0*c6\n
@@ -36,11 +37,13 @@ MMU3:>S0*c6\n
```
Once communication is established the MMU responds with:
+
```
MMU3:S1*ad\n
MMU3:M1*{CRC8};
MMU3:<---nothing---
@@ -61,10 +65,10 @@ MMU3:T0*{CRC8}\n
@@ -96,6 +102,7 @@ MMU3:>C0*{CRC8}\n
```
The MMU will feed a few more millimeters of filament for the extruder gears to grab. When done, the MMU sends:
+
```
MMU3:>Q0*{CRC8}\n
MMU3: FinishingMoves
@@ -103,18 +110,20 @@ MMU3: FinishingMoves
After the `T0*P9` response we immediately continue with the next G-code which should be one or more extruder moves to feed the filament into the hotend.
-
## FINDA status
+
```
MMU3:>P0*{CRC8}\n
```
If the filament is loaded to the extruder, FINDA status is 1 and the MMU responds with:
+
```
MMU3:L{Filament index}*{CRC8}\n
MMU3:Q0*{CRC8}\n
```
The MMU will respond with status messages:
+
```
MMU3:Q0*{CRC8}\n
MMU3: Address of register in hexidecimal
+ * A Address of register in hexadecimal
*
* Example:
* M707 A0x1b - Read a 8bit integer from register 0x1b and prints the result onto the serial line.
@@ -115,7 +115,7 @@ void GcodeSuite::M707() {
* M708 [ A | X ]
*
* Parameters:
- * A Address of register in hexidecimal
+ * A Address of register in hexadecimal
* X Data to write (16-bit integer). Default value 0
*
* Example:
diff --git a/Marlin/src/gcode/motion/G2_G3.cpp b/Marlin/src/gcode/motion/G2_G3.cpp
index c3c2758af4..e8051d2c2a 100644
--- a/Marlin/src/gcode/motion/G2_G3.cpp
+++ b/Marlin/src/gcode/motion/G2_G3.cpp
@@ -309,7 +309,7 @@ void plan_arc(
// a) is <= any configured maximum speed,
// b) does not require centripetal force greater than any configured maximum acceleration,
// c) is <= nominal speed,
- // d) allows the print head to stop in the remining length of the curve within all configured maximum accelerations.
+ // d) allows the print head to stop in the remaining length of the curve within all configured maximum accelerations.
// The last has to be calculated every time through the loop.
const float limiting_accel = _MIN(planner.settings.max_acceleration_mm_per_s2[axis_p], planner.settings.max_acceleration_mm_per_s2[axis_q]),
limiting_speed = _MIN(planner.settings.max_feedrate_mm_s[axis_p], planner.settings.max_feedrate_mm_s[axis_q]),
diff --git a/Marlin/src/gcode/temp/M306.cpp b/Marlin/src/gcode/temp/M306.cpp
index e0c9d935e3..b3f5b6b97b 100644
--- a/Marlin/src/gcode/temp/M306.cpp
+++ b/Marlin/src/gcode/temp/M306.cpp
@@ -39,7 +39,7 @@
* F Ambient heat transfer coefficient (fan on full).
* H Filament heat capacity per mm.
* P Heater power.
- * R Sensor responsiveness (= transfer coefficient / heat capcity).
+ * R Sensor responsiveness (= transfer coefficient / heat capacity).
*
* With MPC_AUTOTUNE:
* T Autotune the extruder specified with 'E' or the active extruder.
@@ -65,7 +65,7 @@ void GcodeSuite::M306() {
default: tuning_type = Temperature::MPCTuningType::AUTO; break;
}
if (TERN0(MPC_PTC, tuning_type == Temperature::MPCTuningType::FORCE_ASYMPTOTIC))
- SERIAL_ECHOLNPGM("Aymptotic tuning not avaiable for PTC hotends");
+ SERIAL_ECHOLNPGM("Asymptotic tuning not available for PTC hotends");
else {
LCD_MESSAGE(MSG_MPC_AUTOTUNE);
thermalManager.MPC_autotune(e, tuning_type);
diff --git a/Marlin/src/inc/Conditionals-4-adv.h b/Marlin/src/inc/Conditionals-4-adv.h
index 66897abc84..0967f994ce 100644
--- a/Marlin/src/inc/Conditionals-4-adv.h
+++ b/Marlin/src/inc/Conditionals-4-adv.h
@@ -1486,7 +1486,7 @@
#if MB(MKS_MONSTER8_V1, BTT_SKR_MINI_E3_V1_0, BTT_SKR_MINI_E3_V1_2, BTT_SKR_MINI_E3_V2_0, BTT_SKR_MINI_E3_V3_0, BTT_SKR_MINI_E3_V3_0_1, BTT_SKR_E3_TURBO, BTT_OCTOPUS_V1_1, BTT_SKR_V3_0, BTT_SKR_V3_0_EZ, AQUILA_V101)
#define LCD_SERIAL_PORT 1
- #elif MB(CREALITY_V24S1_301, CREALITY_V24S1_301F4, CREALITY_F401RE, CREALITY_V423, CREALITY_CR4NTXXC10, CREALITY_CR4NS, MKS_ROBIN, PANOWIN_CUTLASS, KODAMA_BARDO)
+ #elif MB(CREALITY_V24S1_301, CREALITY_V24S1_301F4, CREALITY_F401RE, CREALITY_V422_GD32_MFL, CREALITY_V423, CREALITY_V427_GD32_MFL, CREALITY_CR4NTXXC10, CREALITY_CR4NS, MKS_ROBIN, PANOWIN_CUTLASS, KODAMA_BARDO)
#define LCD_SERIAL_PORT 2
#else
#define LCD_SERIAL_PORT 3
diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h
index 1ff1e2b641..8ac9ce4db9 100644
--- a/Marlin/src/inc/SanityCheck.h
+++ b/Marlin/src/inc/SanityCheck.h
@@ -77,18 +77,20 @@
* tell you about new options that you might find useful. So it's recommended to transfer
* your settings to new Configuration files matching your Marlin version as soon as possible.
*/
-#define HEXIFY(H) _CAT(0x,H)
-#if !defined(CONFIGURATION_H_VERSION) || HEXIFY(CONFIGURATION_H_VERSION) < HEXIFY(REQUIRED_CONFIGURATION_H_VERSION)
- #error "Your Configuration.h file is for an old version of Marlin. Downgrade Marlin or upgrade your Configuration.h."
-#elif HEXIFY(CONFIGURATION_H_VERSION) > HEXIFY(REQUIRED_CONFIGURATION_H_VERSION)
- #error "Your Configuration.h file is for a newer version of Marlin. Upgrade Marlin or downgrade your Configuration.h."
-#endif
-#if !defined(CONFIGURATION_ADV_H_VERSION) || HEXIFY(CONFIGURATION_ADV_H_VERSION) < HEXIFY(REQUIRED_CONFIGURATION_ADV_H_VERSION)
- #error "Your Configuration_adv.h file is for an old version of Marlin. Downgrade Marlin or upgrade your Configuration_adv.h."
-#elif HEXIFY(CONFIGURATION_ADV_H_VERSION) > HEXIFY(REQUIRED_CONFIGURATION_ADV_H_VERSION)
- #error "Your Configuration_adv.h file is for a newer version of Marlin. Upgrade Marlin or downgrade your Configuration_adv.h."
-#endif
-#undef HEXIFY
+#if USE_STD_CONFIGS
+ #define HEXIFY(H) _CAT(0x,H)
+ #if !defined(CONFIGURATION_H_VERSION) || HEXIFY(CONFIGURATION_H_VERSION) < HEXIFY(REQUIRED_CONFIGURATION_H_VERSION)
+ #error "Your Configuration.h file is for an old version of Marlin. Downgrade Marlin or upgrade your Configuration.h."
+ #elif HEXIFY(CONFIGURATION_H_VERSION) > HEXIFY(REQUIRED_CONFIGURATION_H_VERSION)
+ #error "Your Configuration.h file is for a newer version of Marlin. Upgrade Marlin or downgrade your Configuration.h."
+ #endif
+ #if !defined(CONFIGURATION_ADV_H_VERSION) || HEXIFY(CONFIGURATION_ADV_H_VERSION) < HEXIFY(REQUIRED_CONFIGURATION_ADV_H_VERSION)
+ #error "Your Configuration_adv.h file is for an old version of Marlin. Downgrade Marlin or upgrade your Configuration_adv.h."
+ #elif HEXIFY(CONFIGURATION_ADV_H_VERSION) > HEXIFY(REQUIRED_CONFIGURATION_ADV_H_VERSION)
+ #error "Your Configuration_adv.h file is for a newer version of Marlin. Upgrade Marlin or downgrade your Configuration_adv.h."
+ #endif
+ #undef HEXIFY
+#endif // HAS_IGNORED_CONFIGS
/**
* Warnings for old configurations
diff --git a/Marlin/src/inc/Version.h b/Marlin/src/inc/Version.h
index f5d6bd5083..116e85e811 100644
--- a/Marlin/src/inc/Version.h
+++ b/Marlin/src/inc/Version.h
@@ -42,7 +42,7 @@
* version was tagged.
*/
#ifndef STRING_DISTRIBUTION_DATE
- #define STRING_DISTRIBUTION_DATE "2025-08-26"
+ #define STRING_DISTRIBUTION_DATE "2025-09-09"
#endif
/**
diff --git a/Marlin/src/lcd/e3v2/marlinui/ui_status_480x272.cpp b/Marlin/src/lcd/e3v2/marlinui/ui_status_480x272.cpp
index c0b7ba90ec..8c9b51e029 100644
--- a/Marlin/src/lcd/e3v2/marlinui/ui_status_480x272.cpp
+++ b/Marlin/src/lcd/e3v2/marlinui/ui_status_480x272.cpp
@@ -290,17 +290,17 @@ void MarlinUI::draw_status_screen() {
);
}
- uint16_t hx = STATUS_HEATERS_X;
+ uint16_t shx = STATUS_HEATERS_X;
#if HAS_HOTEND
- _draw_heater_status(H_E0, hx, STATUS_HEATERS_Y);
- hx += STATUS_HEATERS_XSPACE;
+ _draw_heater_status(H_E0, shx, STATUS_HEATERS_Y);
+ shx += STATUS_HEATERS_XSPACE;
#endif
#if HAS_MULTI_HOTEND
- _draw_heater_status(H_E1, hx, STATUS_HEATERS_Y);
- hx += STATUS_HEATERS_XSPACE;
+ _draw_heater_status(H_E1, shx, STATUS_HEATERS_Y);
+ shx += STATUS_HEATERS_XSPACE;
#endif
#if HAS_HEATED_BED
- _draw_heater_status(H_BED, hx, STATUS_HEATERS_Y);
+ _draw_heater_status(H_BED, shx, STATUS_HEATERS_Y);
#endif
#if HAS_FAN
_draw_fan_status(LCD_PIXEL_WIDTH - STATUS_CHR_WIDTH * 5, STATUS_FAN_Y);
diff --git a/Marlin/src/lcd/e3v2/proui/gcode_preview.cpp b/Marlin/src/lcd/e3v2/proui/gcode_preview.cpp
index 045615f3c2..9ad098f149 100644
--- a/Marlin/src/lcd/e3v2/proui/gcode_preview.cpp
+++ b/Marlin/src/lcd/e3v2/proui/gcode_preview.cpp
@@ -83,7 +83,7 @@ fileprop_t fileprop;
void getValue(const char * const buf, PGM_P const key, float &value) {
if (value != 0.0f) return;
- char *posptr = strstr_P(buf, key);
+ const char *posptr = strstr_P(buf, key);
if (posptr == nullptr) return;
char num[10] = "";
@@ -101,8 +101,9 @@ void getValue(const char * const buf, PGM_P const key, float &value) {
bool Preview::hasPreview() {
const char * const tbstart = PSTR("; thumbnail begin " STRINGIFY(THUMBWIDTH) "x" STRINGIFY(THUMBHEIGHT));
- char *posptr = nullptr;
+ const char *posptr = nullptr;
uint32_t indx = 0;
+ uint32_t prev_indx = 0;
float tmp = 0;
fileprop.clear();
@@ -114,7 +115,7 @@ bool Preview::hasPreview() {
uint8_t nbyte = 1;
while (!fileprop.thumbstart && nbyte > 0 && indx < 4 * sizeof(buf)) {
nbyte = card.read(buf, sizeof(buf) - 1);
- if (nbyte > 0) {
+ if (nbyte >= 0) {
buf[nbyte] = '\0';
getValue(buf, PSTR(";TIME:"), fileprop.time);
getValue(buf, PSTR(";Filament used:"), fileprop.filament);
@@ -132,13 +133,17 @@ bool Preview::hasPreview() {
fileprop.height -= tmp;
posptr = strstr_P(buf, tbstart);
if (posptr != nullptr) {
- fileprop.thumbstart = indx + (posptr - &buf[0]);
+ fileprop.thumbstart = indx + (posptr - buf);
}
else {
indx += _MAX(10, nbyte - (signed)strlen_P(tbstart));
+ if (indx <= prev_indx) break;
+ prev_indx = indx;
card.setIndex(indx);
}
}
+ else
+ break;
}
if (!fileprop.thumbstart) {
@@ -190,7 +195,7 @@ void Preview::drawFromSD() {
return;
}
- MString<45> buf;
+ TString buf;
dwinDrawRectangle(1, hmiData.colorBackground, 0, 0, DWIN_WIDTH, STATUS_Y - 1);
if (fileprop.time) {
buf.setf(F("Estimated time: %i:%02i"), (uint16_t)fileprop.time / 3600, ((uint16_t)fileprop.time % 3600) / 60);
@@ -210,7 +215,7 @@ void Preview::drawFromSD() {
}
DWINUI::drawButton(BTN_Print, 26, 290);
DWINUI::drawButton(BTN_Cancel, 146, 290);
- show();
+ if (fileprop.thumbsize) show();
drawSelectHighlight(true, 290);
dwinUpdateLCD();
}
diff --git a/Marlin/src/lcd/e3v2/proui/menus.h b/Marlin/src/lcd/e3v2/proui/menus.h
index d1f5cdb698..1d3a394710 100644
--- a/Marlin/src/lcd/e3v2/proui/menus.h
+++ b/Marlin/src/lcd/e3v2/proui/menus.h
@@ -37,14 +37,14 @@
#endif
typedef struct {
- int32_t maxValue = 0; // Auxiliar max integer/scaled float value
- int32_t minValue = 0; // Auxiliar min integer/scaled float value
- int8_t dp = 0; // Auxiliar decimal places
- int32_t value = 0; // Auxiliar integer / scaled float value
- int16_t *intPtr = nullptr; // Auxiliar pointer to 16 bit integer variable
- float *floatPtr = nullptr; // Auxiliar pointer to float variable
- void (*apply)() = nullptr; // Auxiliar apply function
- void (*liveUpdate)() = nullptr; // Auxiliar live update function
+ int32_t maxValue = 0; // Auxiliary max integer/scaled float value
+ int32_t minValue = 0; // Auxiliary min integer/scaled float value
+ int8_t dp = 0; // Auxiliary decimal places
+ int32_t value = 0; // Auxiliary integer / scaled float value
+ int16_t *intPtr = nullptr; // Auxiliary pointer to 16 bit integer variable
+ float *floatPtr = nullptr; // Auxiliary pointer to float variable
+ void (*apply)() = nullptr; // Auxiliary apply function
+ void (*liveUpdate)() = nullptr; // Auxiliary live update function
} MenuData_t;
extern MenuData_t menuData;
diff --git a/Marlin/src/lcd/extui/anycubic_chiron/FileNavigator.cpp b/Marlin/src/lcd/extui/anycubic_chiron/FileNavigator.cpp
index ffc485a3dc..5cbeb193e0 100644
--- a/Marlin/src/lcd/extui/anycubic_chiron/FileNavigator.cpp
+++ b/Marlin/src/lcd/extui/anycubic_chiron/FileNavigator.cpp
@@ -28,7 +28,7 @@
* (not affiliated with Anycubic, Ltd.)
*
* The AC panel wants files in block of 4 and can only display a flat list
- * This library allows full folder traversal or flat file display and supports both standerd and new style panels.
+ * This library allows full folder traversal or flat file display and supports both standard and new style panels.
*
* ## Old Style TFT panel
* Supported chars {}[]-+=_"$%^&*()~<>|
diff --git a/Marlin/src/lcd/extui/anycubic_vyper/dgus_tft.cpp b/Marlin/src/lcd/extui/anycubic_vyper/dgus_tft.cpp
index 696cfad684..af5e20d3a1 100644
--- a/Marlin/src/lcd/extui/anycubic_vyper/dgus_tft.cpp
+++ b/Marlin/src/lcd/extui/anycubic_vyper/dgus_tft.cpp
@@ -496,7 +496,7 @@ namespace Anycubic {
#endif
case AC_printer_printing:
case AC_printer_paused:
- // Heater timout, send acknowledgement
+ // Heater timeout, send acknowledgement
if (strcmp_P(msg, MARLIN_msg_heater_timeout) == 0) {
pause_state = AC_paused_heater_timed_out;
tftSendLn(AC_msg_paused); // enable continue button
diff --git a/Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/README.md b/Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/README.md
index 6ba985d600..61e5f3a388 100644
--- a/Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/README.md
+++ b/Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/README.md
@@ -1,5 +1,4 @@
-FTDI EVE Library
-----------------
+## FTDI EVE Library
The FTDI EVE Library is a fully open-source library and UI framework for the FTDI
FT800 and FT810 graphics processor.
diff --git a/Marlin/src/lcd/extui/ftdi_eve_touch_ui/generic/move_axis_screen.cpp b/Marlin/src/lcd/extui/ftdi_eve_touch_ui/generic/move_axis_screen.cpp
index bbb1d33098..6ed557ba09 100644
--- a/Marlin/src/lcd/extui/ftdi_eve_touch_ui/generic/move_axis_screen.cpp
+++ b/Marlin/src/lcd/extui/ftdi_eve_touch_ui/generic/move_axis_screen.cpp
@@ -75,7 +75,7 @@ void MoveAxisScreen::onRedraw(draw_mode_t what) {
#endif
w.increments();
#ifdef PARKING_COMMAND_GCODE
- if (!ExtUI::isPrinting()) { // making sure the Tool Head Swap Position is not avalible while printing
+ if (!ExtUI::isPrinting()) { // making sure the Tool Head Swap Position is not available while printing
cmd.font(font_medium)
.colors(normal_btn)
.tag(25).button(BTN_POS(1,(7+EXTRUDERS)), BTN_SIZE(13,1), GET_TEXT_F(MSG_TOOL_HEAD_SWAP));
diff --git a/Marlin/src/lcd/menu/menu_x_twist.cpp b/Marlin/src/lcd/menu/menu_x_twist.cpp
index a9998e3e44..8ee261cc97 100644
--- a/Marlin/src/lcd/menu/menu_x_twist.cpp
+++ b/Marlin/src/lcd/menu/menu_x_twist.cpp
@@ -159,7 +159,7 @@ void xatc_wizard_goto_next_point() {
z_offset /= XATC_MAX_POINTS;
// Subtract the average from the values found with this wizard.
- // This way they are indipendent from the z-offset
+ // This way they are independent from the z-offset
for (uint8_t i = 0; i < XATC_MAX_POINTS; ++i) xatc.z_offset[i] -= z_offset;
ui.goto_screen(xatc_wizard_update_z_offset);
diff --git a/Marlin/src/lcd/tft/canvas.cpp b/Marlin/src/lcd/tft/canvas.cpp
index 2486179ce3..d10ca626ec 100644
--- a/Marlin/src/lcd/tft/canvas.cpp
+++ b/Marlin/src/lcd/tft/canvas.cpp
@@ -162,7 +162,7 @@ void Canvas::addImage(int16_t x, int16_t y, MarlinImage image, uint16_t *colors)
rle_state.dsty = dsty;
rle_state.srcx = srcx;
rle_state.srcy = srcy;
- rle_state.rle_offset = bytedata - (uint8_t *)images[image].data;; // Keep these for skipping full RLE decode on future iteratons
+ rle_state.rle_offset = bytedata - (uint8_t *)images[image].data;; // Keep these for skipping full RLE decode on future iterations
}
uint8_t count = *bytedata++; // Get the count byte
diff --git a/Marlin/src/lcd/tft/tft_string.cpp b/Marlin/src/lcd/tft/tft_string.cpp
index edf326cb97..d390ad64f3 100644
--- a/Marlin/src/lcd/tft/tft_string.cpp
+++ b/Marlin/src/lcd/tft/tft_string.cpp
@@ -122,7 +122,7 @@ void TFT_String::add_glyphs(const uint8_t *font) {
}
glyph_t *TFT_String::glyph(uint16_t character) {
- if (character == 0x2026) character = 0x0a; /* character 0x2026 "…" is remaped to 0x0a and should be part of symbols font */
+ if (character == 0x2026) character = 0x0a; /* character 0x2026 "…" is remapped to 0x0a and should be part of symbols font */
if (character < 0x00ff) return glyphs[character] ?: glyphs['?']; /* Use '?' for unknown glyphs */
#if EXTRA_GLYPHS
diff --git a/Marlin/src/module/ft_motion.cpp b/Marlin/src/module/ft_motion.cpp
index 1cc90776d6..d5865913ab 100644
--- a/Marlin/src/module/ft_motion.cpp
+++ b/Marlin/src/module/ft_motion.cpp
@@ -42,7 +42,7 @@ ft_command_t FTMotion::stepperCmdBuff[FTM_STEPPERCMD_BUFF_SIZE] = {0U}; // Stepp
int32_t FTMotion::stepperCmdBuff_produceIdx = 0, // Index of next stepper command write to the buffer.
FTMotion::stepperCmdBuff_consumeIdx = 0; // Index of next stepper command read from the buffer.
-bool FTMotion::sts_stepperBusy = false; // The stepper buffer has items and is in use.
+bool FTMotion::stepperCmdBuffHasData = false; // The stepper buffer has items and is in use.
XYZEval FTMotion::axis_move_end_ti = { 0 };
AxisBits FTMotion::axis_move_dir;
@@ -64,9 +64,9 @@ bool FTMotion::batchRdyForInterp = false; // Indicates the batch is done b
// ... if applicable, and is ready to be converted to step commands.
// Trapezoid data variables.
-xyze_pos_t FTMotion::startPosn, // (mm) Start position of block
- FTMotion::endPosn_prevBlock = { 0.0f }; // (mm) End position of previous block
-xyze_float_t FTMotion::ratio; // (ratio) Axis move ratio of block
+xyze_pos_t FTMotion::startPos, // (mm) Start position of block
+ FTMotion::endPos_prevBlock = { 0.0f }; // (mm) End position of previous block
+xyze_float_t FTMotion::ratio; // (ratio) Axis move ratio of block
float FTMotion::accel_P, // Acceleration prime of block. [mm/sec/sec]
FTMotion::decel_P, // Deceleration prime of block. [mm/sec/sec]
FTMotion::F_P, // Feedrate prime of block. [mm/sec]
@@ -135,7 +135,7 @@ void FTMotion::loop() {
* 4. Signal ready for new block.
*/
if (stepper.abort_current_block) {
- if (sts_stepperBusy) return; // Wait until motion buffers are emptied
+ if (stepperCmdBuffHasData) return; // Wait until motion buffers are emptied
discard_planner_block_protected();
reset();
stepper.abort_current_block = false; // Abort finished.
@@ -149,13 +149,13 @@ void FTMotion::loop() {
continue;
}
loadBlockData(stepper.current_block);
+ blockProcRdy = true;
// If the endstop is already pressed, endstop interrupts won't invoke
// endstop_triggered and the move will grind. So check here for a
// triggered endstop, which shortly marks the block for discard.
endstops.update();
- blockProcRdy = true;
// Some kinematics track axis motion in HX, HY, HZ
#if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY, MARKFORGED_YX)
stepper.last_direction_bits.hx = stepper.current_block->direction_bits.hx;
@@ -170,18 +170,14 @@ void FTMotion::loop() {
if (blockProcRdy) {
- if (!batchRdy) makeVector(); // Caution: Do not consolidate checks on blockProcRdy/batchRdy, as they are written by makeVector().
- // When makeVector is finished: either blockProcRdy has been set false (because the block is
- // done being processed) or batchRdy is set true, or both.
+ if (!batchRdy) makeVector(); // may clear blockProcRdy
// Check if the block has been completely converted:
if (!blockProcRdy) {
discard_planner_block_protected();
-
- // Check if the block needs to be runout:
if (!batchRdy && !planner.has_blocks_queued()) {
runoutBlock();
- makeVector(); // Do an additional makeVector call to guarantee batchRdy set this loop.
+ makeVector(); // Additional call to guarantee batchRdy is set this loop.
}
}
}
@@ -220,7 +216,7 @@ void FTMotion::loop() {
}
// Report busy status to planner.
- busy = (sts_stepperBusy || blockProcRdy || batchRdy || batchRdyForInterp);
+ busy = (stepperCmdBuffHasData || blockProcRdy || batchRdy || batchRdyForInterp);
}
@@ -386,7 +382,7 @@ void FTMotion::reset() {
blockProcRdy = batchRdy = batchRdyForInterp = false;
- endPosn_prevBlock.reset();
+ endPos_prevBlock.reset();
makeVector_idx = 0;
makeVector_batchIdx = TERN(FTM_UNIFIED_BWS, 0, _MIN(BATCH_SIDX_IN_WINDOW, FTM_BATCH_SIZE));
@@ -424,13 +420,13 @@ void FTMotion::discard_planner_block_protected() {
/**
* Set up a pseudo block to allow motion to settle and buffers to empty.
* Called when the planner has one block left. The buffers will be filled
- * with the last commanded position by setting the startPosn block variable to
+ * with the last commanded position by setting the startPos block variable to
* the last position of the previous block and all ratios to zero such that no
* axes' positions are incremented.
*/
void FTMotion::runoutBlock() {
- startPosn = endPosn_prevBlock;
+ startPos = endPos_prevBlock;
ratio.reset();
const int32_t n_to_fill_batch = (FTM_WINDOW_SIZE) - makeVector_batchIdx;
@@ -466,19 +462,9 @@ void FTMotion::loadBlockData(block_t * const current_block) {
const float totalLength = current_block->millimeters,
oneOverLength = 1.0f / totalLength;
- startPosn = endPosn_prevBlock;
- const xyze_pos_t moveDist = LOGICAL_AXIS_ARRAY(
- current_block->steps.e * planner.mm_per_step[block_extruder_axis] * (current_block->direction_bits.e ? 1 : -1),
- current_block->steps.x * planner.mm_per_step[X_AXIS] * (current_block->direction_bits.x ? 1 : -1),
- current_block->steps.y * planner.mm_per_step[Y_AXIS] * (current_block->direction_bits.y ? 1 : -1),
- current_block->steps.z * planner.mm_per_step[Z_AXIS] * (current_block->direction_bits.z ? 1 : -1),
- current_block->steps.i * planner.mm_per_step[I_AXIS] * (current_block->direction_bits.i ? 1 : -1),
- current_block->steps.j * planner.mm_per_step[J_AXIS] * (current_block->direction_bits.j ? 1 : -1),
- current_block->steps.k * planner.mm_per_step[K_AXIS] * (current_block->direction_bits.k ? 1 : -1),
- current_block->steps.u * planner.mm_per_step[U_AXIS] * (current_block->direction_bits.u ? 1 : -1),
- current_block->steps.v * planner.mm_per_step[V_AXIS] * (current_block->direction_bits.v ? 1 : -1),
- current_block->steps.w * planner.mm_per_step[W_AXIS] * (current_block->direction_bits.w ? 1 : -1)
- );
+ startPos = endPos_prevBlock;
+
+ const xyze_pos_t& moveDist = current_block->dist_mm;
ratio = moveDist * oneOverLength;
@@ -561,22 +547,19 @@ void FTMotion::loadBlockData(block_t * const current_block) {
// Accel + Coasting + Decel datapoints
max_intervals = N1 + N2 + N3;
- endPosn_prevBlock += moveDist;
+ endPos_prevBlock += moveDist;
// Watch endstops until the move ends
const millis_t move_end_ti = millis() + SEC_TO_MS((FTM_TS) * float(max_intervals + num_samples_shaper_settle() + ((PROP_BATCHES) + 1) * (FTM_BATCH_SIZE)) + (float(FTM_STEPPERCMD_BUFF_SIZE) / float(FTM_STEPPER_FS)));
- #define __SET_MOVE_END(A,V) do{ if (V) { axis_move_end_ti.A = move_end_ti; axis_move_dir.A = (V > 0); } }while(0);
- #define _SET_MOVE_END(A) __SET_MOVE_END(A, moveDist[_AXIS(A)])
- #if CORE_IS_XY
- __SET_MOVE_END(X, moveDist.x + moveDist.y);
- __SET_MOVE_END(Y, moveDist.x - moveDist.y);
- #else
- _SET_MOVE_END(X);
- _SET_MOVE_END(Y);
- #endif
- TERN_(HAS_Z_AXIS, _SET_MOVE_END(Z));
- SECONDARY_AXIS_MAP(_SET_MOVE_END);
+ #define _SET_MOVE_END(A) do{ \
+ if (moveDist.A) { \
+ axis_move_end_ti.A = move_end_ti; \
+ axis_move_dir.A = moveDist.A > 0; \
+ } \
+ }while(0);
+
+ LOGICAL_AXIS_MAP(_SET_MOVE_END);
}
// Generate data points of the trajectory.
@@ -605,7 +588,7 @@ void FTMotion::makeVector() {
TERN_(HAS_EXTRUDERS, accel_k = decel_P); // (mm/s^2) Acceleration K factor from Decel phase
}
- #define _SET_TRAJ(q) traj.q[makeVector_batchIdx] = startPosn.q + ratio.q * dist;
+ #define _SET_TRAJ(q) traj.q[makeVector_batchIdx] = startPos.q + ratio.q * dist;
LOGICAL_AXIS_MAP_LC(_SET_TRAJ);
#if HAS_EXTRUDERS
diff --git a/Marlin/src/module/ft_motion.h b/Marlin/src/module/ft_motion.h
index feaea55ca1..9d3d5c9833 100644
--- a/Marlin/src/module/ft_motion.h
+++ b/Marlin/src/module/ft_motion.h
@@ -113,7 +113,7 @@ class FTMotion {
static int32_t stepperCmdBuff_produceIdx, // Index of next stepper command write to the buffer.
stepperCmdBuff_consumeIdx; // Index of next stepper command read from the buffer.
- static bool sts_stepperBusy; // The stepper buffer has items and is in use.
+ static bool stepperCmdBuffHasData; // The stepper buffer has items and is in use.
static XYZEval axis_move_end_ti;
static AxisBits axis_move_dir;
@@ -145,9 +145,9 @@ class FTMotion {
static bool batchRdy, batchRdyForInterp;
// Trapezoid data variables.
- static xyze_pos_t startPosn, // (mm) Start position of block
- endPosn_prevBlock; // (mm) End position of previous block
- static xyze_float_t ratio; // (ratio) Axis move ratio of block
+ static xyze_pos_t startPos, // (mm) Start position of block
+ endPos_prevBlock; // (mm) End position of previous block
+ static xyze_float_t ratio; // (ratio) Axis move ratio of block
static float accel_P, decel_P,
F_P,
f_s,
diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp
index 156ab142f9..2918f14bd2 100644
--- a/Marlin/src/module/planner.cpp
+++ b/Marlin/src/module/planner.cpp
@@ -142,7 +142,7 @@ planner_settings_t Planner::settings; // Initialized by settings.load
/**
* Set up inline block variables
- * Set laser_power_floor based on SPEED_POWER_MIN to pevent a zero power output state with LASER_POWER_TRAP
+ * Set laser_power_floor based on SPEED_POWER_MIN to prevent a zero power output state with LASER_POWER_TRAP
*/
#if ENABLED(LASER_FEATURE)
laser_state_t Planner::laser_inline; // Current state for blocks
@@ -2097,6 +2097,8 @@ bool Planner::_populate_block(
TERN_(BACKLASH_COMPENSATION, backlash.add_correction_steps(dist, dm, block));
}
+ TERN_(FT_MOTION, block->dist_mm = dist_mm); // Store the distance for all axes in mm for this block
+
TERN_(HAS_EXTRUDERS, block->steps.e = esteps);
block->step_event_count = (
@@ -2507,7 +2509,7 @@ bool Planner::_populate_block(
* Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
* Let a circle be tangent to both previous and current path line segments, where the junction
* deviation is defined as the distance from the junction to the closest edge of the circle,
- * colinear with the circle center. The circular segment joining the two paths represents the
+ * collinear with the circle center. The circular segment joining the two paths represents the
* path of centripetal acceleration. Solve for max velocity based on max acceleration about the
* radius of the circle, defined indirectly by junction deviation. This may be also viewed as
* path width or max_jerk in the previous Grbl version. This approach does not actually deviate
@@ -3191,7 +3193,7 @@ void Planner::set_machine_position_mm(const abce_pos_t &abce) {
/**
* Set the machine positions in millimeters (soon) given the native position.
- * Synchonized with the planner queue.
+ * Synchronized with the planner queue.
*
* - Modifiers are applied for skew, leveling, retract, etc.
* - Kinematics are applied to remap cartesian positions to stepper positions.
diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h
index eb8f2ed17f..2695c1768e 100644
--- a/Marlin/src/module/planner.h
+++ b/Marlin/src/module/planner.h
@@ -266,6 +266,10 @@ typedef struct PlannerBlock {
AxisBits direction_bits; // Direction bits set for this block, where 1 is negative motion
+ #if ENABLED(FT_MOTION)
+ xyze_pos_t dist_mm; // The distance traveled in mm along each axis
+ #endif
+
// Advance extrusion
#if ENABLED(LIN_ADVANCE)
#if ENABLED(SMOOTH_LIN_ADVANCE)
diff --git a/Marlin/src/module/planner_bezier.cpp b/Marlin/src/module/planner_bezier.cpp
index a3f98435d0..7789b616b8 100644
--- a/Marlin/src/module/planner_bezier.cpp
+++ b/Marlin/src/module/planner_bezier.cpp
@@ -49,7 +49,7 @@ static inline float interp(const_float_t a, const_float_t b, const_float_t t) {
* Compute a Bézier curve using the De Casteljau's algorithm (see
* https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm), which is
* easy to code and has good numerical stability (very important,
- * since Arudino works with limited precision real numbers).
+ * since Arduino works with limited precision real numbers).
*/
static inline float eval_bezier(const_float_t a, const_float_t b, const_float_t c, const_float_t d, const_float_t t) {
const float iab = interp(a, b, t),
diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp
index ad83329f05..f2698b3da5 100644
--- a/Marlin/src/module/stepper.cpp
+++ b/Marlin/src/module/stepper.cpp
@@ -2474,7 +2474,7 @@ hal_timer_t Stepper::block_phase_isr() {
* isPowered - True when a move is powered.
* isEnabled - laser power is active.
*
- * Laser power variables are calulated and stored in this block by the planner code.
+ * Laser power variables are calculated and stored in this block by the planner code.
* trap_ramp_active_pwr - the active power in this block across accel or decel trap steps.
* trap_ramp_entry_incr - holds the precalculated value to increase the current power per accel step.
*/
@@ -3565,8 +3565,8 @@ void Stepper::report_positions() {
void Stepper::ftMotion_stepper() {
// Check if the buffer is empty.
- ftMotion.sts_stepperBusy = (ftMotion.stepperCmdBuff_produceIdx != ftMotion.stepperCmdBuff_consumeIdx);
- if (!ftMotion.sts_stepperBusy) return;
+ ftMotion.stepperCmdBuffHasData = (ftMotion.stepperCmdBuff_produceIdx != ftMotion.stepperCmdBuff_consumeIdx);
+ if (!ftMotion.stepperCmdBuffHasData) return;
// "Pop" one command from current motion buffer
const ft_command_t command = ftMotion.stepperCmdBuff[ftMotion.stepperCmdBuff_consumeIdx];
diff --git a/Marlin/src/module/stepper/cycles.h b/Marlin/src/module/stepper/cycles.h
index b01d199cd8..fa76f60127 100644
--- a/Marlin/src/module/stepper/cycles.h
+++ b/Marlin/src/module/stepper/cycles.h
@@ -38,7 +38,7 @@
* ...as measured on an LPC1768 with a scope and converted to cycles.
* Not applicable to other 32-bit processors, but as long as others
* take longer, pulses will be longer. For example the SKR Pro
- * (stm32f407zgt6) requires ~60 cyles.
+ * (stm32f407zgt6) requires ~60 cycles.
*/
constexpr uint32_t timer_read_add_and_store_cycles = 34UL;
diff --git a/Marlin/src/module/stepper/indirection.h b/Marlin/src/module/stepper/indirection.h
index 19594df66c..05adff7a4e 100644
--- a/Marlin/src/module/stepper/indirection.h
+++ b/Marlin/src/module/stepper/indirection.h
@@ -53,7 +53,7 @@
* Q_STEP_INIT() Q_STEP_WRITE(S) Q_STEP_READ()
*
* Steppers may not have an enable state or may be enabled by other methods
- * beyond a single pin (SOFTWARE_DRIVER_ENABLE) so these can be overriden:
+ * beyond a single pin (SOFTWARE_DRIVER_ENABLE) so these can be overridden:
* ENABLE_STEPPER_Q() DISABLE_STEPPER_Q()
*
* Axis Stepper Control (X Y Z I J K U V W)
diff --git a/Marlin/src/module/thermistor/thermistor_61.h b/Marlin/src/module/thermistor/thermistor_61.h
index 2916bffd3b..5a82341a19 100644
--- a/Marlin/src/module/thermistor/thermistor_61.h
+++ b/Marlin/src/module/thermistor/thermistor_61.h
@@ -31,7 +31,7 @@
// B Value 3950K at 25/50 deg. C
// B Value Tolerance + / - 1%
constexpr temp_entry_t temptable_61[] PROGMEM = {
- { OV( 2.00), 420 }, // Guestimate to ensure we don't lose a reading and drop temps to -50 when over
+ { OV( 2.00), 420 }, // Guesstimate to ensure we don't lose a reading and drop temps to -50 when over
{ OV( 12.07), 350 },
{ OV( 12.79), 345 },
{ OV( 13.59), 340 },
diff --git a/Marlin/src/pins/ramps/pins_RAMPS.h b/Marlin/src/pins/ramps/pins_RAMPS.h
index 6b4795adff..f3aecb247b 100644
--- a/Marlin/src/pins/ramps/pins_RAMPS.h
+++ b/Marlin/src/pins/ramps/pins_RAMPS.h
@@ -864,7 +864,7 @@
#elif ENABLED(MINIPANEL)
#ifndef BEEPER_PIN
- #define BEEPER_PIN AUX2_08_PIN
+ #define BEEPER_PIN AUX2_08
#endif
#define LCD_BACKLIGHT_PIN AUX2_10
diff --git a/Marlin/src/pins/stm32f1/pins_CREALITY_V4.h b/Marlin/src/pins/stm32f1/pins_CREALITY_V4.h
index a0e36c7f1b..34b1e633df 100644
--- a/Marlin/src/pins/stm32f1/pins_CREALITY_V4.h
+++ b/Marlin/src/pins/stm32f1/pins_CREALITY_V4.h
@@ -326,13 +326,26 @@
// Changing these will not change the pin they are on.
// Hardware UART pins
-#define UART1_TX_PIN PA9 // default uses CH340 RX
-#define UART1_RX_PIN PA10 // default uses CH340 TX
-#define UART2_TX_PIN PA2 // default uses HEATER_BED_PIN
-#define UART2_RX_PIN PA3 // not connected
-#define UART3_TX_PIN PB10 // default uses LCD connector
-#define UART3_RX_PIN PB11 // default uses LCD connector
-#define UART4_TX_PIN PC10 // default uses sdcard SDIO_D2
-#define UART4_RX_PIN PC11 // default uses sdcard SDIO_D3
-#define UART5_TX_PIN PC12 // default uses sdcard SDIO_CK
-#define UART5_RX_PIN PD2 // default uses sdcard SDIO_CMD
+#ifdef ARDUINO_ARCH_MFL // GD32 MFL UARTs start from 0
+ #define UART0_TX_PIN PA9 // default uses CH340 RX
+ #define UART0_RX_PIN PA10 // default uses CH340 TX
+ #define UART1_TX_PIN PA2 // default uses HEATER_BED_PIN
+ #define UART1_RX_PIN PA3 // not connected
+ #define UART2_TX_PIN PB10 // default uses LCD connector
+ #define UART2_RX_PIN PB11 // default uses LCD connector
+ #define UART3_TX_PIN PC10 // default uses sdcard SDIO_D2
+ #define UART3_RX_PIN PC11 // default uses sdcard SDIO_D3
+ #define UART4_TX_PIN PC12 // default uses sdcard SDIO_CK
+ #define UART4_RX_PIN PD2 // default uses sdcard SDIO_CMD
+#else
+ #define UART1_TX_PIN PA9 // default uses CH340 RX
+ #define UART1_RX_PIN PA10 // default uses CH340 TX
+ #define UART2_TX_PIN PA2 // default uses HEATER_BED_PIN
+ #define UART2_RX_PIN PA3 // not connected
+ #define UART3_TX_PIN PB10 // default uses LCD connector
+ #define UART3_RX_PIN PB11 // default uses LCD connector
+ #define UART4_TX_PIN PC10 // default uses sdcard SDIO_D2
+ #define UART4_RX_PIN PC11 // default uses sdcard SDIO_D3
+ #define UART5_TX_PIN PC12 // default uses sdcard SDIO_CK
+ #define UART5_RX_PIN PD2 // default uses sdcard SDIO_CMD
+#endif
diff --git a/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h b/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h
index 1b5aad04e1..f452fc1ebd 100644
--- a/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h
+++ b/Marlin/src/pins/stm32g0/pins_BTT_EBB42_V1_1.h
@@ -93,7 +93,7 @@
*/
//#define E0_HARDWARE_SERIAL MSerial4
- // This is the stable default value after testing, but, higher UART rates could be configured, remeber to test the Steppers with the M122 command to check if everything works.
+ // This is the stable default value after testing, but, higher UART rates could be configured, remember to test the Steppers with the M122 command to check if everything works.
//#define TMC_BAUD_RATE 250000
#define E0_SERIAL_TX_PIN PA15
diff --git a/Marlin/src/sd/SdBaseFile.cpp b/Marlin/src/sd/SdBaseFile.cpp
index 27437355ba..3610457dad 100644
--- a/Marlin/src/sd/SdBaseFile.cpp
+++ b/Marlin/src/sd/SdBaseFile.cpp
@@ -255,10 +255,12 @@ bool SdBaseFile::exists(const char *name) {
* If no data is read, fgets() returns zero for EOF or -1 if an error occurred.
*/
int16_t SdBaseFile::fgets(char *str, int16_t num, char *delim) {
+ if (!str || num <= 1) return -1; // Ensure space for at least '\0'
+
char ch;
int16_t n = 0;
int16_t r = -1;
- while ((n + 1) < num && (r = read(&ch, 1)) == 1) {
+ while (n < num - 1 && (r = read(&ch, 1)) == 1) {
// delete CR
if (ch == '\r') continue;
str[n++] = ch;
@@ -269,10 +271,7 @@ int16_t SdBaseFile::fgets(char *str, int16_t num, char *delim) {
if (strchr(delim, ch)) break;
}
}
- if (r < 0) {
- // read error
- return -1;
- }
+ if (r < 0) return -1; // read error
str[n] = '\0';
return n;
}
@@ -342,7 +341,9 @@ int8_t SdBaseFile::lsPrintNext(const uint8_t flags, const uint8_t indent) {
uint8_t w = 0;
while (1) {
- if (read(&dir, sizeof(dir)) != sizeof(dir)) return 0;
+ const int16_t r = read(&dir, sizeof(dir));
+ if (r < 0) return -1;
+ if (r != sizeof(dir)) return 0;
if (dir.name[0] == DIR_NAME_FREE) return 0;
// skip deleted entry and entries for . and ..
@@ -1277,7 +1278,8 @@ int SdBaseFile::peek() {
filepos_t pos;
getpos(&pos);
int c = read();
- if (c >= 0) setpos(&pos);
+ if (c < 0) return -1;
+ setpos(&pos);
return c;
}
@@ -1346,8 +1348,10 @@ bool SdBaseFile::printName() {
* If an error occurs or end of file is reached -1 is returned.
*/
int16_t SdBaseFile::read() {
- uint8_t b;
- return read(&b, 1) == 1 ? b : -1;
+ uint8_t b = 0;
+ const int16_t r = read(&b, 1);
+ if (r != 1) return -1;
+ return static_cast(b);
}
/**
diff --git a/README.md b/README.md
index cc5fa60db7..90815578c8 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@
### 🌍 Translations
+
| Aragonés |
@@ -69,7 +70,7 @@ Please test this firmware and let us know if it misbehaves in any way. Volunteer
## Marlin 2.1 Bugfix Branch
-__Not for production use. Use with caution!__
+**Not for production use. Use with caution!**
Marlin 2.1 continues to support both 32-bit ARM and 8-bit AVR boards while adding support for up to 9 coordinated axes and to up to 8 extruders.
@@ -103,32 +104,32 @@ Every new HAL opens up a world of hardware. At this time we need HALs for RP2040
### Supported Platforms
- Platform|MCU|Example Boards
- --------|---|-------
- [Arduino AVR](//www.arduino.cc/)|ATmega|RAMPS, Melzi, RAMBo
- [Teensy++ 2.0](//www.microchip.com/en-us/product/AT90USB1286)|AT90USB1286|Printrboard
- [Arduino Due](//www.arduino.cc/en/Guide/ArduinoDue)|SAM3X8E|RAMPS-FD, RADDS, RAMPS4DUE
- [ESP32](//github.com/espressif/arduino-esp32)|ESP32|FYSETC E4, E4d@BOX, MRR
- [GD32](//www.gigadevice.com/)|GD32 ARM Cortex-M4|Creality MFL GD32 V4.2.2
- [HC32](//www.huazhoucn.com/)|HC32|Ender-2 Pro, Voxelab Aquila
- [LPC1768](//www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/512-kb-flash-64-kb-sram-ethernet-usb-lqfp100-package:LPC1768FBD100)|ARM® Cortex-M3|MKS SBASE, Re-ARM, Selena Compact
- [LPC1769](//www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/512-kb-flash-64-kb-sram-ethernet-usb-lqfp100-package:LPC1769FBD100)|ARM® Cortex-M3|Smoothieboard, Azteeg X5 mini, TH3D EZBoard
- [Pico RP2040](//www.raspberrypi.com/documentation/microcontrollers/pico-series.html)|Dual Cortex M0+|BigTreeTech SKR Pico
- [STM32F103](//www.st.com/en/microcontrollers-microprocessors/stm32f103.html)|ARM® Cortex-M3|Malyan M200, GTM32 Pro, MKS Robin, BTT SKR Mini
- [STM32F401](//www.st.com/en/microcontrollers-microprocessors/stm32f401.html)|ARM® Cortex-M4|ARMED, Rumba32, SKR Pro, Lerdge, FYSETC S6, Artillery Ruby
- [STM32F7x6](//www.st.com/en/microcontrollers-microprocessors/stm32f7x6.html)|ARM® Cortex-M7|The Borg, RemRam V1
- [STM32G0B1RET6](//www.st.com/en/microcontrollers-microprocessors/stm32g0x1.html)|ARM® Cortex-M0+|BigTreeTech SKR mini E3 V3.0
- [STM32H743xIT6](//www.st.com/en/microcontrollers-microprocessors/stm32h743-753.html)|ARM® Cortex-M7|BigTreeTech SKR V3.0, SKR EZ V3.0, SKR SE BX V2.0/V3.0
- [SAMD21P20A](//www.adafruit.com/product/4064)|ARM® Cortex-M0+|Adafruit Grand Central M4
- [SAMD51P20A](//www.adafruit.com/product/4064)|ARM® Cortex-M4|Adafruit Grand Central M4
- [Teensy 3.2/3.1](//www.pjrc.com/teensy/teensy31.html)|MK20DX256VLH7 ARM® Cortex-M4|
- [Teensy 3.5](//www.pjrc.com/store/teensy35.html)|MK64FX512-VMD12 ARM® Cortex-M4|
- [Teensy 3.6](//www.pjrc.com/store/teensy36.html)|MK66FX1MB-VMD18 ARM® Cortex-M4|
- [Teensy 4.0](//www.pjrc.com/store/teensy40.html)|MIMXRT1062-DVL6B ARM® Cortex-M7|
- [Teensy 4.1](//www.pjrc.com/store/teensy41.html)|MIMXRT1062-DVJ6B ARM® Cortex-M7|
- Linux Native|x86 / ARM / RISC-V|Raspberry Pi GPIO
- Simulator|Windows, macOS, Linux|Desktop OS
- [All supported boards](//marlinfw.org/docs/hardware/boards.html#boards-list)|All platforms|All boards
+| Platform | MCU | Example Boards |
+| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- | ---------------------------------------------------------- |
+| [Arduino AVR](//www.arduino.cc/) | ATmega | RAMPS, Melzi, RAMBo |
+| [Teensy++ 2.0](//www.microchip.com/en-us/product/AT90USB1286) | AT90USB1286 | Printrboard |
+| [Arduino Due](//www.arduino.cc/en/Guide/ArduinoDue) | SAM3X8E | RAMPS-FD, RADDS, RAMPS4DUE |
+| [ESP32](//github.com/espressif/arduino-esp32) | ESP32 | FYSETC E4, E4d@BOX, MRR |
+| [GD32](//www.gigadevice.com/) | GD32 ARM Cortex-M4 | Creality MFL GD32 V4.2.2 |
+| [HC32](//www.huazhoucn.com/) | HC32 | Ender-2 Pro, Voxelab Aquila |
+| [LPC1768](//www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/512-kb-flash-64-kb-sram-ethernet-usb-lqfp100-package:LPC1768FBD100) | ARM® Cortex-M3 | MKS SBASE, Re-ARM, Selena Compact |
+| [LPC1769](//www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/512-kb-flash-64-kb-sram-ethernet-usb-lqfp100-package:LPC1769FBD100) | ARM® Cortex-M3 | Smoothieboard, Azteeg X5 mini, TH3D EZBoard |
+| [Pico RP2040](//www.raspberrypi.com/documentation/microcontrollers/pico-series.html) | Dual Cortex M0+ | BigTreeTech SKR Pico |
+| [STM32F103](//www.st.com/en/microcontrollers-microprocessors/stm32f103.html) | ARM® Cortex-M3 | Malyan M200, GTM32 Pro, MKS Robin, BTT SKR Mini |
+| [STM32F401](//www.st.com/en/microcontrollers-microprocessors/stm32f401.html) | ARM® Cortex-M4 | ARMED, Rumba32, SKR Pro, Lerdge, FYSETC S6, Artillery Ruby |
+| [STM32F7x6](//www.st.com/en/microcontrollers-microprocessors/stm32f7x6.html) | ARM® Cortex-M7 | The Borg, RemRam V1 |
+| [STM32G0B1RET6](//www.st.com/en/microcontrollers-microprocessors/stm32g0x1.html) | ARM® Cortex-M0+ | BigTreeTech SKR mini E3 V3.0 |
+| [STM32H743xIT6](//www.st.com/en/microcontrollers-microprocessors/stm32h743-753.html) | ARM® Cortex-M7 | BigTreeTech SKR V3.0, SKR EZ V3.0, SKR SE BX V2.0/V3.0 |
+| [SAMD21P20A](//www.adafruit.com/product/4064) | ARM® Cortex-M0+ | Adafruit Grand Central M4 |
+| [SAMD51P20A](//www.adafruit.com/product/4064) | ARM® Cortex-M4 | Adafruit Grand Central M4 |
+| [Teensy 3.2/3.1](//www.pjrc.com/teensy/teensy31.html) | MK20DX256VLH7 ARM® Cortex-M4 |
+| [Teensy 3.5](//www.pjrc.com/store/teensy35.html) | MK64FX512-VMD12 ARM® Cortex-M4 |
+| [Teensy 3.6](//www.pjrc.com/store/teensy36.html) | MK66FX1MB-VMD18 ARM® Cortex-M4 |
+| [Teensy 4.0](//www.pjrc.com/store/teensy40.html) | MIMXRT1062-DVL6B ARM® Cortex-M7 |
+| [Teensy 4.1](//www.pjrc.com/store/teensy41.html) | MIMXRT1062-DVJ6B ARM® Cortex-M7 |
+| Linux Native | x86 / ARM / RISC-V | Raspberry Pi GPIO |
+| Simulator | Windows, macOS, Linux | Desktop OS |
+| [All supported boards](//marlinfw.org/docs/hardware/boards.html#boards-list) | All platforms | All boards |
## Marlin Support
@@ -173,17 +174,17 @@ Marlin Firmware original logo design by Ahmet Cem TURAN [@ahmetcemturan](//githu
## Project Leadership
-Name|Role|Link|Donate
-----|----|----|----
-🇺🇸 Scott Lahteine|Project Lead|[[@thinkyhead](//github.com/thinkyhead)]|[💸 Donate](//marlinfw.org/docs/development/contributing.html#donate)
-🇺🇸 Roxanne Neufeld|Admin|[[@Roxy-3D](//github.com/Roxy-3D)]|
-🇺🇸 Keith Bennett|Admin|[[@thisiskeithb](//github.com/thisiskeithb)]|[💸 Donate](//github.com/sponsors/thisiskeithb)
-🇺🇸 Jason Smith|Admin|[[@sjasonsmith](//github.com/sjasonsmith)]|
-🇧🇷 Victor Oliveira|Admin|[[@rhapsodyv](//github.com/rhapsodyv)]|
-🇬🇧 Chris Pepper|Admin|[[@p3p](//github.com/p3p)]|
-🇳🇿 Peter Ellens|Admin|[[@ellensp](//github.com/ellensp)]|[💸 Donate](//ko-fi.com/ellensp)
-🇺🇸 Bob Kuhn|Admin|[[@Bob-the-Kuhn](//github.com/Bob-the-Kuhn)]|
-🇳🇱 Erik van der Zalm|Founder|[[@ErikZalm](//github.com/ErikZalm)]|
+| Name | Role | Link | Donate |
+| -------------------- | ------------ | -------------------------------------------- | --------------------------------------------------------------------- |
+| 🇺🇸 Scott Lahteine | Project Lead | [[@thinkyhead](//github.com/thinkyhead)] | [💸 Donate](//marlinfw.org/docs/development/contributing.html#donate) |
+| 🇺🇸 Roxanne Neufeld | Admin | [[@Roxy-3D](//github.com/Roxy-3D)] |
+| 🇺🇸 Keith Bennett | Admin | [[@thisiskeithb](//github.com/thisiskeithb)] | [💸 Donate](//github.com/sponsors/thisiskeithb) |
+| 🇺🇸 Jason Smith | Admin | [[@sjasonsmith](//github.com/sjasonsmith)] |
+| 🇧🇷 Victor Oliveira | Admin | [[@rhapsodyv](//github.com/rhapsodyv)] |
+| 🇬🇧 Chris Pepper | Admin | [[@p3p](//github.com/p3p)] |
+| 🇳🇿 Peter Ellens | Admin | [[@ellensp](//github.com/ellensp)] | [💸 Donate](//ko-fi.com/ellensp) |
+| 🇺🇸 Bob Kuhn | Admin | [[@Bob-the-Kuhn](//github.com/Bob-the-Kuhn)] |
+| 🇳🇱 Erik van der Zalm | Founder | [[@ErikZalm](//github.com/ErikZalm)] |
## Star History
diff --git a/buildroot/share/dwin/bin/README.md b/buildroot/share/dwin/bin/README.md
index c78f4eb97f..b439d53905 100644
--- a/buildroot/share/dwin/bin/README.md
+++ b/buildroot/share/dwin/bin/README.md
@@ -42,13 +42,15 @@ These tools must be run from a terminal with access to an installed Python 3 and
Pillow is most easily installed with pip:
- python3 -m pip install pillow
+```sh
+python3 -m pip install pillow
+```
## Examples
These tools process an `.ICO` file that you specify. The safest method is to create a folder and copy your `.ICO` file there. For example:
-```
+```sh
$ mkdir hackicons
$ cp 7.ICO hackicons
$ cd hackicons
@@ -61,11 +63,13 @@ The following explanations will refer back to this layout.
If you want to edit the individual icons stored in an ICO file (or add more images) you'll first need to extract all the images from the archive using `splitIco.py`.
#### Usage:
-```
+
+```sh
splitIco.py #.ICO foldername
```
#### Splitting .ICO FIle In Windows:
+
- Create `Split-ICO.bat` file in this folder with the following code:
- `for /f %%f in ('dir *.ICO /B /O:-D') do splitico.py %%f %%f-icons`
- Paste `.ICO` file into this folder
@@ -99,11 +103,13 @@ Once the individual JPEG files have been saved they can be edited using common g
If you want to create an ICO file you'll need to use `makeIco.py`.
#### Usage:
-```
+
+```sh
makeIco.py foldername #.ICO
```
#### Making .ICO FIle In Windows:
+
- Create `Make-ICO.bat` file in this folder with the following code:
- ```
setlocal enabledelayedexpansion
diff --git a/buildroot/share/fonts/README.md b/buildroot/share/fonts/README.md
index b80de8e26e..57b4d1f57e 100644
--- a/buildroot/share/fonts/README.md
+++ b/buildroot/share/fonts/README.md
@@ -1,21 +1,23 @@
# Marlin fonts
## Author and license
+
The original author of the following font files is [A. Hardtung](https://github.com/AnHardt).
Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/
- - HD44780_C.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
- - HD44780_J.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
- - HD44780_W.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
- - ISO10646-1.fon ([be79235](https://github.com/MarlinFirmware/Marlin/commit/be79235ef255a5c42fd385820447ec351f23b9b1))
- - ISO10646_5_Cyrillic.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
- - ISO10646_CN.fon ([6b1b718](https://github.com/MarlinFirmware/Marlin/commit/6b1b71837c98ceab55db7433357a13cd829d1ede))
- - ISO10646_Kana.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
- - Marlin_symbols.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
+- HD44780_C.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
+- HD44780_J.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
+- HD44780_W.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
+- ISO10646-1.fon ([be79235](https://github.com/MarlinFirmware/Marlin/commit/be79235ef255a5c42fd385820447ec351f23b9b1))
+- ISO10646_5_Cyrillic.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
+- ISO10646_CN.fon ([6b1b718](https://github.com/MarlinFirmware/Marlin/commit/6b1b71837c98ceab55db7433357a13cd829d1ede))
+- ISO10646_Kana.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
+- Marlin_symbols.fon ([fe2bd23](https://github.com/MarlinFirmware/Marlin/commit/fe2bd237d556439499dfdee852c1550c7a16430a))
Additional changes to the original font files distributed with Marlin are copyrighted under the terms of the [GPLv3](https://www.gnu.org/licenses/gpl-3.0.txt) license.
## Documentation
+
For detailed information about adding new fonts to Marlin [see this article](https://marlinfw.org/docs/development/fonts.html).
diff --git a/buildroot/share/git/README.md b/buildroot/share/git/README.md
index 4dcd7c15a7..c1474c603b 100755
--- a/buildroot/share/git/README.md
+++ b/buildroot/share/git/README.md
@@ -16,42 +16,42 @@ The following scripts can be used on any system with a GNU environment to speed
#### Remotes
-File|Description
-----|-----------
-mfadd [user]|Add and Fetch Remote - Add and fetch another user's Marlin fork. Optionally, check out one of their branches.
-mfinit|Init Working Copy - Create a remote named '`upstream`' (for use by the other scripts) pointing to the '`MarlinFirmware`' fork. This only needs to be used once. Newer versions of Github Desktop may create `upstream` on your behalf.
+| File | Description |
+| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| mfadd [user] | Add and Fetch Remote - Add and fetch another user's Marlin fork. Optionally, check out one of their branches. |
+| mfinit | Init Working Copy - Create a remote named '`upstream`' (for use by the other scripts) pointing to the '`MarlinFirmware`' fork. This only needs to be used once. Newer versions of Github Desktop may create `upstream` on your behalf. |
#### Branches
-File|Description
-----|-----------
-mfnew [branch]|New Branch - Creates a new branch based on `upstream/[PR-target]`. All new work should start with this command.
-mffp|Fast Push - Push the HEAD or a commit ID to `upstream` immediately. Requires privileged access to the MarlinFirmware repo.
-firstpush|Push the current branch to 'origin' -your fork on Github- and set it to track '`origin`'. The branch needs to reside on Github before you can use it to make a PR.
+| File | Description |
+| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| mfnew [branch] | New Branch - Creates a new branch based on `upstream/[PR-target]`. All new work should start with this command. |
+| mffp | Fast Push - Push the HEAD or a commit ID to `upstream` immediately. Requires privileged access to the MarlinFirmware repo. |
+| firstpush | Push the current branch to 'origin' -your fork on Github- and set it to track '`origin`'. The branch needs to reside on Github before you can use it to make a PR. |
#### Making / Amending PRs
-File|Description
-----|-----------
-mfpr|Pull Request - Open the Compare / Pull Request page on Github for the current branch.
-mfrb|Do a `git rebase` then `git rebase -i` of the current branch onto `upstream/[PR-target]`. Use this to edit your commits anytime.
-mfqp|Quick Patch - Commit all current changes as "patch", then do `mfrb`, followed by `git push -f` if no conflicts need resolution.
+| File | Description |
+| ---- | -------------------------------------------------------------------------------------------------------------------------------- |
+| mfpr | Pull Request - Open the Compare / Pull Request page on Github for the current branch. |
+| mfrb | Do a `git rebase` then `git rebase -i` of the current branch onto `upstream/[PR-target]`. Use this to edit your commits anytime. |
+| mfqp | Quick Patch - Commit all current changes as "patch", then do `mfrb`, followed by `git push -f` if no conflicts need resolution. |
#### Documentation
-File|Description
-----|-----------
-mfdoc|Build the documentation with Jekyll and preview it locally.
-mfpub|Build and publish the documentation to marlinfw.org.
+| File | Description |
+| ----- | ----------------------------------------------------------- |
+| mfdoc | Build the documentation with Jekyll and preview it locally. |
+| mfpub | Build and publish the documentation to marlinfw.org. |
#### Utilities
-File|Description
-----|-----------
-ghtp -[h/s]|Set the protocol to use for all remotes. -h for HTTPS, -s for SSL.
-ghpc [-f]|Push current branch to 'origin' or to the remote indicated by the error.
-mfinfo|This utility script is used by the other scripts to get:
- The upstream project ('`MarlinFirmware`')
- the '`origin`' project (i.e., your Github username),
- the repository name ('`Marlin`'),
- the PR target branch ('`bugfix-1.1.x`'), and
- the current branch (or the first command-line argument).
By itself, `mfinfo` simply prints these values to the console.
-mfclean |Prune your merged and remotely-deleted branches.
+| File | Description |
+| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| ghtp -[h/s] | Set the protocol to use for all remotes. -h for HTTPS, -s for SSL. |
+| ghpc [-f] | Push current branch to 'origin' or to the remote indicated by the error. |
+| mfinfo | This utility script is used by the other scripts to get:
- The upstream project ('`MarlinFirmware`')
- the '`origin`' project (i.e., your Github username),
- the repository name ('`Marlin`'),
- the PR target branch ('`bugfix-1.1.x`'), and
- the current branch (or the first command-line argument).
By itself, `mfinfo` simply prints these values to the console. |
+| mfclean | Prune your merged and remotely-deleted branches. |
---
diff --git a/buildroot/share/scripts/linesformat.py b/buildroot/share/scripts/linesformat.py
new file mode 100755
index 0000000000..4cbe8190f5
--- /dev/null
+++ b/buildroot/share/scripts/linesformat.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+"""
+Formatter script for *.h, *.c, *.cpp, *.md, *.txt, and other source files
+
+usage: linesformat.py [infile] [outfile]
+
+With no parameters convert STDIN to STDOUT
+"""
+
+import sys, re, argparse
+
+do_log = False
+def logmsg(msg, line):
+ if do_log: print(msg, line)
+
+def format_text(argv):
+ parser = argparse.ArgumentParser(description="Formatter script for source files")
+ parser.add_argument('infile', nargs='?', default=None, help="Input file to read from. If not provided, reads from stdin.")
+ parser.add_argument('outfile', nargs='?', default=None, help="Output file to write to. If not provided, writes to stdout.")
+ parser.add_argument('-v', action='store_true', help="Enable logging.")
+
+ args = parser.parse_args(argv)
+
+ src_file = args.infile or 'stdin'
+ dst_file = args.outfile or None
+
+ scnt = 0
+ for arg in argv:
+ if arg == '-v':
+ global do_log
+ do_log = args.v or True
+ elif scnt == 0:
+ # Get a source file if specified. Default destination is the same file
+ src_file = dst_file = arg
+ scnt += 1
+ elif scnt == 1:
+ # Get destination file if specified
+ dst_file = arg
+ scnt += 1
+
+ # No text to process yet
+ file_text = ''
+
+ if src_file == 'stdin':
+ # If no source file specified read from STDIN
+ file_text = sys.stdin.read()
+ else:
+ # Directory will be processed recursively with editorconfig
+ if os.path.isdir(src_file):
+ apply_editorconfig_rules(src_file)
+ return
+
+ # Open and read the file src_file
+ with open(src_file, 'r', encoding='utf-8') as rf: file_text = rf.read()
+
+ if len(file_text) == 0:
+ print('No text to process')
+ return
+
+ # Read from file or STDIN until it terminates
+ filtered = re.sub(r'\s+$', '', file_text) + '\n'
+ if dst_file:
+ with open(dst_file, 'w', encoding='utf-8') as wf: wf.write(filtered)
+ else:
+ print(filtered)
+
+# Python standard startup for command line with arguments
+if __name__ == '__main__':
+ format_text(sys.argv[1:])
+
+# Usage
+apply_editorconfig_rules('/path/to/your/folder')
diff --git a/buildroot/share/scripts/pinsformat.py b/buildroot/share/scripts/pinsformat.py
index 41949c42cc..d9712bbc63 100755
--- a/buildroot/share/scripts/pinsformat.py
+++ b/buildroot/share/scripts/pinsformat.py
@@ -38,7 +38,7 @@ mpatt = [ r'-?\d{1,3}', r'P[A-I]\d+', r'P\d_\d+', r'Pin[A-Z]\d\b' ]
mstr = '|'.join(mpatt)
mexpr = [ re.compile(f'^{m}$') for m in mpatt ]
-# Corrsponding padding for each pattern
+# Corresponding padding for each pattern
ppad = [ 3, 4, 5, 5 ]
# Match a define line
diff --git a/docs/BinaryFileTransferProtocol.md b/docs/BinaryFileTransferProtocol.md
index 6d52671789..ebb1194aac 100644
--- a/docs/BinaryFileTransferProtocol.md
+++ b/docs/BinaryFileTransferProtocol.md
@@ -1,10 +1,13 @@
# Marlin Binary File Transfer (BFT)
+
Marlin is capable of transferring binary data to the internal storage (SD card) via serial when built with `BINARY_FILE_TRANSFER` enabled. The following is a description of the binary protocol that must be used to conduct transfers once the printer is in binary mode after running `M28 B1`.
## Data Endianness
+
All data structures are **little-endian**! This means that when constructing the packets with multi-byte values, the lower bits are packed first. For example, each packet should start with a 16-bit start token with the value of `0xB5AD`. The data itself should start with a value of `0xAD` followed by `0xB5` etc.
An example Connection SYNC packet, which is only a header and has no payload:
+
```
S S P P P H
t y r a a e
@@ -35,16 +38,17 @@ ADB5 00 0 1 0000 0103
+-------------------------------+-------------------------------+
```
-| Field | Width | Description |
-|-----------------|---------|---|
-| Start Token | 16 bits | Each packet must start with the 16-bit value `0xB5AD`. |
-| Sync Number | 8 bits | Synchronization value, each packet after sync should increment this value by 1. |
-| Protocol ID | 4 bits | Protocol ID. `0` for Connection Control, `1` for Transfer. See Below. |
-| Packet Type | 4 bits | Packet Type ID. Depends on the Protocol ID, see below. |
+| Field | Width | Description |
+| --------------- | ------- | ------------------------------------------------------------------------------------------------- |
+| Start Token | 16 bits | Each packet must start with the 16-bit value `0xB5AD`. |
+| Sync Number | 8 bits | Synchronization value, each packet after sync should increment this value by 1. |
+| Protocol ID | 4 bits | Protocol ID. `0` for Connection Control, `1` for Transfer. See Below. |
+| Packet Type | 4 bits | Packet Type ID. Depends on the Protocol ID, see below. |
| Payload Length | 16 bits | Length of payload data. If this value is greater than 0, a packet payload will follow the header. |
-| Header Checksum | 16 bits | 16-bit Fletchers checksum of the header data excluding the Start Token |
+| Header Checksum | 16 bits | 16-bit Fletchers checksum of the header data excluding the Start Token |
## Packet Payload
+
If the Payload Length field of the header is non-zero, payload data is expected to follow.
```
@@ -57,15 +61,17 @@ If the Payload Length field of the header is non-zero, payload data is expected
+-------------------------------+-------------------------------+
```
-| Field | Width | Description |
-|-----------------|------------------------|---|
-| Payload Data | Payload Length bytes | Payload data. This should be no longer than the buffer length reported by the Connection SYNC operation. |
-| Packet Checksum | 16 bits | 16-bit Fletchers checksum of the header and payload, including the Header Checksum, but excluding the Start Token. |
+| Field | Width | Description |
+| --------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------ |
+| Payload Data | Payload Length bytes | Payload data. This should be no longer than the buffer length reported by the Connection SYNC operation. |
+| Packet Checksum | 16 bits | 16-bit Fletchers checksum of the header and payload, including the Header Checksum, but excluding the Start Token. |
## Fletchers Checksum
+
Data packets require a checksum for the header and a checksum for the entire packet if the packet has a payload. In both cases the checksum does not include the first two bytes of the packet, the Start Token.
A simple example implementation:
+
```c++
uint16_t cs = 0;
for (size_t i = 2; i` message. This acknowledgement only signifies the client has received the packet and that the header was well formed. An `ok` acknowledgement does not signify successful operation in cases where the client also sends detailed response messages (see details on packet types below). Most notably, with the current implementation the client will still respond `ok` when a client sends multiple packets with the same Sync Number, but will not send the proper response or any errors.
**NOTE**: The `ok` acknowledgement is sent before any packet type specific output. The `SYNC` value should match the Sync Number of the last packet sent, and the next packet sent should use a Sync Number of this value + 1.
Example:
+
```
ok1
```
### rs
+
In the case of a packet being sent out of order, where the Sync Number is not the previous Sync Number + 1, an `rs` message will be sent with the last Sync Number received.
Example:
+
```
rs1
```
## Connection Control (`Protocol ID` 0)
+
`Protocol ID` 0 packets control the binary connection itself. There are only 2 types:
-| Packet Type | Name | Description |
-|---|---|---|
-| 1 | SYNC | Synchronize host and client and get connection info. |
-| 2 | CLOSE | Close the binary connection and switch back to ASCII. |
+| Packet Type | Name | Description |
+| ----------- | ----- | ----------------------------------------------------- |
+| 1 | SYNC | Synchronize host and client and get connection info. |
+| 2 | CLOSE | Close the binary connection and switch back to ASCII. |
### SYNC Packet
+
A SYNC packet should be the first packet sent by a host after enabling binary mode. On success, a sync response will be sent.
**Note**: This is the only packet that is not acknowledged with an `ok` response.
Returns a sync response:
+
```
ss,,..
```
-| Value | Description |
-|---|---|
-| SYNC | The current Sync Number, this should be used in the next packet sent and incremented by 1 for each packet sent after. |
-| BUFFER_SIZE | The client buffer size. Packet Payload Length must not exceed this value. |
-| VERSION_MAJOR | The major version number of the client Marlin BFT protocol, e.g., `0`. |
-| VERSION_MINOR | The minor version number of the client Marlin BFT protocol, e.g., `1`. |
-| VERSION_PATCH | The patch version number of the client Marlin BFT protocol, e.g., `0`. |
+| Value | Description |
+| ------------- | --------------------------------------------------------------------------------------------------------------------- |
+| SYNC | The current Sync Number, this should be used in the next packet sent and incremented by 1 for each packet sent after. |
+| BUFFER_SIZE | The client buffer size. Packet Payload Length must not exceed this value. |
+| VERSION_MAJOR | The major version number of the client Marlin BFT protocol, e.g., `0`. |
+| VERSION_MINOR | The minor version number of the client Marlin BFT protocol, e.g., `1`. |
+| VERSION_PATCH | The patch version number of the client Marlin BFT protocol, e.g., `0`. |
Example response:
+
```
ss0,96,0.1.0
```
### CLOSE Packet
+
A CLOSE packet should be the last packet sent by a host. On success, the client will switch back to ASCII mode.
-## Transfer Control (`Protocol ID` 1)
+## Transfer Control (`Protocol ID` 1)
+
`Protocol ID` 1 packets control the file transfers performed over the connection:
-| Packet Type | Name | Description |
-|---|---|---|
-| 0 | QUERY | Query the client protocol details and compression parameters. |
-| 1 | OPEN | Open a file for writing and begin accepting data to transfer. |
-| 2 | CLOSE | Finish writing and close the current file. |
-| 3 | WRITE | Write data to an open file. |
-| 4 | ABORT | Abort file transfer. |
+| Packet Type | Name | Description |
+| ----------- | ----- | ------------------------------------------------------------- |
+| 0 | QUERY | Query the client protocol details and compression parameters. |
+| 1 | OPEN | Open a file for writing and begin accepting data to transfer. |
+| 2 | CLOSE | Finish writing and close the current file. |
+| 3 | WRITE | Write data to an open file. |
+| 4 | ABORT | Abort file transfer. |
### QUERY Packet
+
A QUERY packet should be the second packet sent by a host, after a SYNC packet. On success a query response will be sent in addition to an `ok` acknowledgement.
Returns a query response:
+
```
PFT:version:..:compression:(,)
```
-| Value | Description |
-|---|---|
-| VERSION_MAJOR | The major version number of the client Marlin BFT protocol, e.g., `0`. |
-| VERSION_MINOR | The minor version number of the client Marlin BFT protocol, e.g., `1`. |
-| VERSION_PATCH | The patch version number of the client Marlin BFT protocol, e.g., `0`. |
-| COMPRESSION_ALGO | Compression algorithm. Currently either `heatshrink` or `none` |
+| Value | Description |
+| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| VERSION_MAJOR | The major version number of the client Marlin BFT protocol, e.g., `0`. |
+| VERSION_MINOR | The minor version number of the client Marlin BFT protocol, e.g., `1`. |
+| VERSION_PATCH | The patch version number of the client Marlin BFT protocol, e.g., `0`. |
+| COMPRESSION_ALGO | Compression algorithm. Currently either `heatshrink` or `none` |
| COMPRESSION_PARAMS | Compression parameters, separated by commas. Currently, if `COMPRESSION_AGLO` is heatshrink, this will be the window size and lookahead size. |
Example response:
+
```
PFT:version:0.1.0:compression:heatshrink,8,4
```
### OPEN Packet
+
Opens a file for writing. The filename and other options are specified in the Packet Payload. The filename can be a long filename if the firmware is compiled with support, however the entire Packet Payload must not be longer than the buffer length returned by the SYNC Packet. The filename value must include a null terminator.
Payload:
+
```
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@@ -176,50 +197,53 @@ Payload:
+-------------------------------+-------------------------------+
```
-| Field | Width | Description |
-|-----------------|------------------------|---|
-| Dummy | 8 bits | A boolean value indicating if this file transfer should be actually carried out or not. If `1`, the client will respond as if the file is opened and accept data transfer, but no data will be written. |
-| Compression | 8 bits | A boolean value indicating if the data to be transferred will be compressed using the algorithm and parameters returned in the QUERY Packet. |
-| Filename | ... | A filename including a null terminator byte. |
-| Packet Checksum | 16 bits | 16-bit Fletchers checksum of the header and payload, including the Header Checksum, but excluding the Start Token. |
+| Field | Width | Description |
+| --------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Dummy | 8 bits | A boolean value indicating if this file transfer should be actually carried out or not. If `1`, the client will respond as if the file is opened and accept data transfer, but no data will be written. |
+| Compression | 8 bits | A boolean value indicating if the data to be transferred will be compressed using the algorithm and parameters returned in the QUERY Packet. |
+| Filename | ... | A filename including a null terminator byte. |
+| Packet Checksum | 16 bits | 16-bit Fletchers checksum of the header and payload, including the Header Checksum, but excluding the Start Token. |
Responses:
-| Response | Description |
-|---|---|
-| `PFT:success` | File opened and ready for write. |
+| Response | Description |
+| ------------- | ---------------------------------- |
+| `PFT:success` | File opened and ready for write. |
| `PFT:fail` | The client couldn't open the file. |
-| `PFT:busy` | The file is already open. |
+| `PFT:busy` | The file is already open. |
### CLOSE Packet
+
Closes the currently open file.
Responses:
-| Response | Description |
-|---|---|
+| Response | Description |
+| ------------- | ------------------------------- |
| `PFT:success` | Buffer flushed and file closed. |
-| `PFT:ioerror` | Client storage device failure. |
-| `PFT:invalid` | No file open. |
+| `PFT:ioerror` | Client storage device failure. |
+| `PFT:invalid` | No file open. |
### WRITE Packet
+
Writes payload data to the currently open file. If the file was opened with Compression set to 1, the data will be decompressed first. Payload Length must not exceed the buffer size returned by the SYNC Packet.
Responses:
On success, an `ok` response will be sent. On error, an `ok` response will be followed by an error response:
-| Response | Description |
-|---|---|
+| Response | Description |
+| ------------- | ------------------------------ |
| `PFT:ioerror` | Client storage device failure. |
-| `PFT:invalid` | No file open. |
+| `PFT:invalid` | No file open. |
### ABORT Packet
+
Closes the currently open file and remove it.
Responses:
-| Response | Description |
-|---|---|
+| Response | Description |
+| ------------- | ------------------------------- |
| `PFT:success` | Transfer aborted, file removed. |
## Typical Usage
diff --git a/docs/Bresenham.md b/docs/Bresenham.md
index 2d57a1cdab..6916e35f48 100644
--- a/docs/Bresenham.md
+++ b/docs/Bresenham.md
@@ -97,7 +97,7 @@ The update rules for the error on each step may also be cast into ε' form. Cons
ε = ε + m - 1
```
- Multiplying through by Δx yields:
+Multiplying through by Δx yields:
```
ε.Δx = ε.Δx + Δy
@@ -111,7 +111,7 @@ Which is in ε' form:
ε' = ε' + Δy - Δx
```
-Using this new ``error'' value, ε' with the new test and update equations gives Bresenham's integer-only line drawing algorithm:
+Using this new ``error'' value, ε' with the new test and update equations gives Bresenham's integer-only line drawing algorithm:
```
ε' = 0, y = y[1]
@@ -125,7 +125,7 @@ for x = x1 to x2 do
endfor
```
-It is a Integer only algorithm - hence efficient (fast). And the Multiplication by 2 can be implemented by left-shift. 0 <= m <= 1
+It is a Integer only algorithm - hence efficient (fast). And the Multiplication by 2 can be implemented by left-shift. 0 <= m <= 1
### Oversampling Bresenham algorithm:
@@ -170,12 +170,14 @@ y + ε + m/r < y + 0.5
Once we did the decision, then the error update conditions are:
Decision A:
+
```
ε[new] = y + ε + m/r - y
ε[new] = ε + m/r [2]
```
Decision B:
+
```
ε[new] = y + ε + m/r - (y+1)
ε[new] = ε + m/r - 1 [3]
diff --git a/docs/ConfigEmbedding.md b/docs/ConfigEmbedding.md
index 562661e111..a9770fb42b 100644
--- a/docs/ConfigEmbedding.md
+++ b/docs/ConfigEmbedding.md
@@ -3,13 +3,16 @@
Starting with version 2.0.9.3, Marlin can automatically extract the configuration used to generate the firmware and store it in the firmware binary. This is enabled by defining `CONFIGURATION_EMBEDDING` in `Configuration_adv.h`.
## How it's done
+
At the start of the PlatformIO build process, we create an embedded configuration by extracting all active options from the Configuration files and writing them out as JSON to `marlin_config.json`, which also includes specific build information (like the git revision, the build date, and some version information). The JSON file is then compressed in a ZIP archive called `.pio/build/mc.zip` which is converted into a C array and stored in a C++ file called `mc.h` which is included in the build.
## Extracting configurations from a Marlin binary
+
To get the configuration out of a binary firmware, you'll need a non-write-protected SD card inserted into the printer while running the firmware.
Send the command `M503 C` to write the file `mc.zip` to the SD card. Copy the file to your computer, ideally in the same folder as the Marlin repository.
Run the following commands to extract and apply the configuration:
+
```
$ git checkout -f
$ unzip mc.zip
diff --git a/docs/Cutter.md b/docs/Cutter.md
index 207036c6e8..ace957210a 100644
--- a/docs/Cutter.md
+++ b/docs/Cutter.md
@@ -92,33 +92,33 @@ Once the entry and exit power values are determined, the values are divided into
trap step power incr_decr = ( cruize power - entry_exit ) / accel_decel_steps
The trap steps are incremented or decremented during each accel or decel step until the block is complete.
-Step power is either cumulatively added or subtracted during trapeziod ramp progressions.
+Step power is either cumulatively added or subtracted during trapezoid ramp progressions.
#### Planner Code:
- ```
- if (block->laser.power > 0) {
- NOLESS(block->laser.power, laser_power_floor);
- block->laser.trap_ramp_active_pwr = (block->laser.power - laser_power_floor) * (initial_rate / float(block->nominal_rate)) + laser_power_floor;
- block->laser.trap_ramp_entry_incr = (block->laser.power - block->laser.trap_ramp_active_pwr) / accelerate_steps;
- float laser_pwr = block->laser.power * (final_rate / float(block->nominal_rate));
- NOLESS(laser_pwr, laser_power_floor);
- block->laser.trap_ramp_exit_decr = (block->laser.power - laser_pwr) / decelerate_steps;
- ```
+```
+if (block->laser.power > 0) {
+ NOLESS(block->laser.power, laser_power_floor);
+ block->laser.trap_ramp_active_pwr = (block->laser.power - laser_power_floor) * (initial_rate / float(block->nominal_rate)) + laser_power_floor;
+ block->laser.trap_ramp_entry_incr = (block->laser.power - block->laser.trap_ramp_active_pwr) / accelerate_steps;
+ float laser_pwr = block->laser.power * (final_rate / float(block->nominal_rate));
+ NOLESS(laser_pwr, laser_power_floor);
+ block->laser.trap_ramp_exit_decr = (block->laser.power - laser_pwr) / decelerate_steps;
+```
#### Stepper Code:
- ```
- if (current_block->laser.trap_ramp_entry_incr > 0) {
- cutter.apply_power(current_block->laser.trap_ramp_active_pwr);
- current_block->laser.trap_ramp_active_pwr += current_block->laser.trap_ramp_entry_incr;
- ```
+```
+if (current_block->laser.trap_ramp_entry_incr > 0) {
+ cutter.apply_power(current_block->laser.trap_ramp_active_pwr);
+ current_block->laser.trap_ramp_active_pwr += current_block->laser.trap_ramp_entry_incr;
+```
- ```
- if (current_block->laser.trap_ramp_exit_decr > 0) {
- current_block->laser.trap_ramp_active_pwr -= current_block->laser.trap_ramp_exit_decr;
- cutter.apply_power(current_block->laser.trap_ramp_active_pwr);
- ```
+```
+if (current_block->laser.trap_ramp_exit_decr > 0) {
+ current_block->laser.trap_ramp_active_pwr -= current_block->laser.trap_ramp_exit_decr;
+ cutter.apply_power(current_block->laser.trap_ramp_active_pwr);
+```
### Dynamic Inline Calculations
diff --git a/docs/Queue.md b/docs/Queue.md
index 6d4fb9d041..e0f50e7807 100644
--- a/docs/Queue.md
+++ b/docs/Queue.md
@@ -13,6 +13,7 @@ Whenever the command queue gets full the sender needs to wait for space to open
An opposite problem called "planner starvation" occurs when Marlin receives many short and fast moves in a row so the Planner Buffer gets completed very quickly. In this case the host can't send commands fast enough to prevent the Planner Buffer from emptying out. Planner starvation causes obvious stuttering and is commonly seen on overloaded deltabots during small curves. Marlin has strategies to mitigate this issue, but sometimes a model has to be re-sliced (or the G-code has to be post-processed with Arc Welder) just to stay within the machine's inherent limits.
Here's a basic flowchart of Marlin command processing:
+
```
+------+ Marlin's GCodeQueue
| | +--------------------------------------+ +-----------+
@@ -39,6 +40,7 @@ Here's a basic flowchart of Marlin command processing:
```
Marlin is a single-threaded application with a main `loop()` that manages the command queue and an `idle()` routine that manages the hardware. The command queue is handled in two stages:
+
1. The `idle()` routine reads all inputs and attempts to enqueue any completed command lines.
2. The main `loop()` gets the command at the front the G-code queue (if any) and runs it. Each G-code command blocks the main loop, preventing the queue from advancing until it returns. To keep essential tasks and the UI running, any commands that run a long process need to call `idle()` frequently.
@@ -51,6 +53,7 @@ If no data is available on the serial buffer, Marlin can be configured to period
## Limitation of the design
Some limitations to the design are evident:
+
1. Whenever the G-code processor is busy processing a command, the G-code queue cannot advance.
2. A long command like `G29` causes commands to pile up and to fill the queue, making the host wait.
3. Each serial input requires a buffer large enough for a complete G-code line. This is set by `MAX_CMD_SIZE` with a default value of 96.
diff --git a/docs/Serial.md b/docs/Serial.md
index 5e0d7e63eb..9ef9a781fa 100644
--- a/docs/Serial.md
+++ b/docs/Serial.md
@@ -3,7 +3,7 @@
Marlin is targeting a plethora of different CPU architectures and platforms. Each of these platforms has its own serial interface.
While many provide a Arduino-like Serial class, it's not all of them, and the differences in the existing API create a very complex brain teaser for writing code that works more or less on each platform.
-Moreover, many platform have intrinsic needs about serial port (like forwarding the output on multiple serial port, providing a *serial-like* telnet server, mixing USB-based serial port with SD card emulation) that are difficult to handle cleanly in the other platform serial logic.
+Moreover, many platform have intrinsic needs about serial port (like forwarding the output on multiple serial port, providing a _serial-like_ telnet server, mixing USB-based serial port with SD card emulation) that are difficult to handle cleanly in the other platform serial logic.
Starting with version 2.0.8, Marlin provides a common interface for its serial needs.
@@ -18,54 +18,64 @@ Instead, the Curiously Recurring Template Pattern (**CRTP**) is used so that, up
Because some platform do not follow the same interface, the missing method in the actual low-level implementation are detected via SFINAE and a wrapper is generated when such method are missing. See the `CALL_IF_EXISTS` macro in `Marlin/src/core/macros.h` for documentation of this technique.
## Composing the desired feature
+
The different specificities for each architecture are provided by composing the serial type based on desired functionality.
In the `Marlin/src/core/serial_hook.h` file, the different serial feature are declared and defined in each templated type:
+
1. `BaseSerial` is a simple 1:1 wrapper to the underlying, Arduino compatible, `Serial`'s class. It derives from it. You'll use this if the platform does not do anything specific for the `Serial` object (for example, if an interrupt callback calls directly the serial **instance** in the platform's framework code, this is not the right class to use). This wrapper is completely inlined so that it does not generate any code upon compilation. `BaseSerial` constructor forwards any parameter to the platform's `Serial`'s constructor.
2. `ForwardSerial` is a composing wrapper. It references an actual Arduino compatible `Serial` instance. You'll use this if the instance is declared in the platform's framework and is being referred directly in the framework. This is not as efficient as the `BaseSerial` implementation since static dereferencing is done for each method call (it'll still be faster than virtual dispatching)
-3. `ConditionalSerial` is working a bit like the `ForwardSerial` interface, but it checks a boolean condition before calling the referenced instance. You'll use it when the serial output can be switch off at runtime, for example in a *telnet* like serial output that should not emit any packet if no client is connected.
+3. `ConditionalSerial` is working a bit like the `ForwardSerial` interface, but it checks a boolean condition before calling the referenced instance. You'll use it when the serial output can be switch off at runtime, for example in a _telnet_ like serial output that should not emit any packet if no client is connected.
4. `RuntimeSerial` is providing a runtime-modifiable hooking method for its `write` and `msgDone` method. You'll use it if you need to capture the serial output of Marlin, for example to display the G-Code parser's output on a GUI interface. The hooking interface is setup via the `setHook` method.
-5. `MultiSerial` is a runtime modifiable serial output multiplexer. It can output (*respectively input*) to 2 different interface based on a port *mask*. You'll use this if you need to output the same serial stream to multiple port. You can plug a `MultiSerial` to itself to duplicate to more than 2 ports.
+5. `MultiSerial` is a runtime modifiable serial output multiplexer. It can output (_respectively input_) to 2 different interface based on a port _mask_. You'll use this if you need to output the same serial stream to multiple port. You can plug a `MultiSerial` to itself to duplicate to more than 2 ports.
## Plumbing
+
Since all the types above are using CRTP, it's possible to combine them to get the appropriate functionality.
This is easily done via type definition of the feature.
For example, to create a single serial interface with 2 serial outputs (one enabled at runtime and the other switchable):
+
```cpp
typedef MultiSerial< RuntimeSerial, ConditionalSerial > Serial1Class;
```
To send the same output to 4 serial ports you could nest `MultiSerial` like this:
+
```cpp
typedef MultiSerial< MultiSerial< BaseSerial, BaseSerial >, MultiSerial< BaseSerial, BaseSerial, 2, 1>, 0, 2> Serial1Class;
```
+
The magical numbers here are the step and offset for computing the serial port. Simplifying the above monster a bit:
+
```cpp
MS< A = MS, B=MS, offset=0, step=2>
```
+
This means that the underlying multiserial A (with output to `a,b`) is available from offset = 0 to offset + step = 1 (default value).
The multiserial B (with output to `c,d`) is available from offset = 2 (the next step from the root multiserial) to offset + step = 3.
In practice, the root multiserial will redirect any index/mask `offset` to `offset + step - 1` to its first leaf, and any index/mask `offset + step` to `offset + 2*step - 1` to its second leaf.
## Emergency parser
+
By default, the serial base interface provide an emergency parser that's only enable for serial classes that support it. Because of this condition, all underlying types take a first `bool emergencyParserEnabled` argument to their constructor. You must take into account this parameter when defining the actual type used.
## SERIAL macros
+
The following macros are defined (in `serial.h`) to output data to the serial ports:
-| MACRO | Parameters | Usage | Example | Expected output |
-|-------|------------|-------|---------|-----------------|
-| `SERIAL_ECHO` | Any basic type is supported (`char`, `uint8_t`, `int16_t`, `int32_t`, `float`, `long`, `const char*`, ...). | For a numeric type it prints the number in decimal. A string is output as a string. | `uint8_t a = 123; SERIAL_ECHO(a); SERIAL_CHAR(' '); SERIAL_ECHO(' '); ` | `123 32` |
-| `SERIAL_ECHOLN` | Same as `SERIAL_ECHO` | Do `SERIAL_ECHO`, adding a newline | `int a = 456; SERIAL_ECHOLN(a);` | `456\n` |
-| `SERIAL_ECHOPGM` | String / Value pairs | Print a series of string literals and values alternately | `SERIAL_ECHOPGM("Bob", 34);` | `Bob34` |
-| `SERIAL_ECHOLNPGM` | Same as `SERIAL_ECHOPGM` | Do `SERIAL_ECHOPGM`, adding a newline | `SERIAL_ECHOPGM("Alice", 56);` | `alice56` |
-| `SERIAL_ECHOPGM_P` | Like `SERIAL_ECHOPGM` but takes PGM strings | Print a series of PGM strings and values alternately | `SERIAL_ECHOPGM_P(GET_TEXT(MSG_HELLO), 123);` | `Hello123` |
-| `SERIAL_ECHOLNPGM_P` | Same as `SERIAL_ECHOPGM_P` | Do `SERIAL_ECHOPGM_P`, adding a newline | `SERIAL_ECHOLNPGM_P(PSTR("Alice"), 78);` | `alice78\n` |
-| `SERIAL_ECHO_START` | None | Prefix an echo line | `SERIAL_ECHO_START();` | `echo:` |
-| `SERIAL_ECHO_MSG` | Same as `SERIAL_ECHOLNPGM` | Print a full echo line | `SERIAL_ECHO_MSG("Count is ", count);` | `echo:Count is 3` |
-| `SERIAL_ERROR_START`| None | Prefix an error line | `SERIAL_ERROR_START();` | `Error:` |
-| `SERIAL_ERROR_MSG` | Same as `SERIAL_ECHOLNPGM` | Print a full error line | `SERIAL_ERROR_MSG("Not found");` | `Error:Not found` |
-| `SERIAL_ECHO_SP` | Number of spaces | Print one or more spaces | `SERIAL_ECHO_SP(3)` | ` ` |
-| `SERIAL_EOL` | None | Print an end of line | `SERIAL_EOL();` | `\n` |
+| MACRO | Parameters | Usage | Example | Expected output |
+| -------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------- |
+| `SERIAL_ECHO` | Any basic type is supported (`char`, `uint8_t`, `int16_t`, `int32_t`, `float`, `long`, `const char*`, ...). | For a numeric type it prints the number in decimal. A string is output as a string. | `uint8_t a = 123; SERIAL_ECHO(a); SERIAL_CHAR(' '); SERIAL_ECHO(' '); ` | `123 32` |
+| `SERIAL_ECHOLN` | Same as `SERIAL_ECHO` | Do `SERIAL_ECHO`, adding a newline | `int a = 456; SERIAL_ECHOLN(a);` | `456\n` |
+| `SERIAL_ECHOPGM` | String / Value pairs | Print a series of string literals and values alternately | `SERIAL_ECHOPGM("Bob", 34);` | `Bob34` |
+| `SERIAL_ECHOLNPGM` | Same as `SERIAL_ECHOPGM` | Do `SERIAL_ECHOPGM`, adding a newline | `SERIAL_ECHOPGM("Alice", 56);` | `alice56` |
+| `SERIAL_ECHOPGM_P` | Like `SERIAL_ECHOPGM` but takes PGM strings | Print a series of PGM strings and values alternately | `SERIAL_ECHOPGM_P(GET_TEXT(MSG_HELLO), 123);` | `Hello123` |
+| `SERIAL_ECHOLNPGM_P` | Same as `SERIAL_ECHOPGM_P` | Do `SERIAL_ECHOPGM_P`, adding a newline | `SERIAL_ECHOLNPGM_P(PSTR("Alice"), 78);` | `alice78\n` |
+| `SERIAL_ECHO_START` | None | Prefix an echo line | `SERIAL_ECHO_START();` | `echo:` |
+| `SERIAL_ECHO_MSG` | Same as `SERIAL_ECHOLNPGM` | Print a full echo line | `SERIAL_ECHO_MSG("Count is ", count);` | `echo:Count is 3` |
+| `SERIAL_ERROR_START` | None | Prefix an error line | `SERIAL_ERROR_START();` | `Error:` |
+| `SERIAL_ERROR_MSG` | Same as `SERIAL_ECHOLNPGM` | Print a full error line | `SERIAL_ERROR_MSG("Not found");` | `Error:Not found` |
+| `SERIAL_ECHO_SP` | Number of spaces | Print one or more spaces | `SERIAL_ECHO_SP(3)` | ` ` |
+| `SERIAL_EOL` | None | Print an end of line | `SERIAL_EOL();` | `\n` |
-*This document was written by [X-Ryl669](https://blog.cyril.by) and is under [CC-SA license](https://creativecommons.org/licenses/by-sa)*
+_This document was written by [X-Ryl669](https://blog.cyril.by) and is under [CC-SA license](https://creativecommons.org/licenses/by-sa)_
diff --git a/test/README.md b/test/README.md
index 19b4cd7d59..3bcd29bcc2 100644
--- a/test/README.md
+++ b/test/README.md
@@ -1,6 +1,7 @@
# Testing Marlin
Marlin included two types of automated tests:
+
- [Build Tests](../buildroot/tests) to catch syntax and code build errors.
- Unit Tests (this folder) to catch implementation errors.
@@ -21,15 +22,19 @@ Generally speaking, the types of errors caught by unit tests are most often caug
### Unit test FAQ
#### Q: Isn't writing unit tests a lot of work?
+
A: Yes, and it can be especially difficult with existing code that wasn't designed for unit testing. Some common sense should be used to decide where to employ unit testing, and at what level to perform it. While unit testing takes effort, it pays dividends in preventing regressions, and helping to pinpoint the source of failures when they do occur.
#### Q: Will this make refactoring harder?
+
A: Yes and No. Of course if you refactor code that unit tests use directly, it will have to be reworked as well. It actually can make refactoring more efficient, by providing assurance that the mechanism still works as intended.
#### Q: How can I debug one of these failing unit tests?
+
A: That's a great question, without a known immediate answer. It is likely possible to debug them interactively through PlatformIO, but that can at times take some creativity to configure. Unit tests are generally extremely small, so even without interactive debugging it can get you fairly close to the cause of the problem.
### Unit test architecture
+
We are currently using [PlatformIO unit tests](https://docs.platformio.org/en/latest/plus/unit-testing.html).
Since Marlin only compiles code required by the configuration, a separate test binary must be generated for any configuration change. The following process is used to unit test a variety of configurations: