diff --git a/Marlin/src/HAL/NATIVE_SIM/HAL.h b/Marlin/src/HAL/NATIVE_SIM/HAL.h
index f741d0b246..ffede7ef34 100644
--- a/Marlin/src/HAL/NATIVE_SIM/HAL.h
+++ b/Marlin/src/HAL/NATIVE_SIM/HAL.h
@@ -52,7 +52,9 @@ uint8_t _getc();
// ------------------------
#define CPU_32_BIT
-#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp
+
+class Servo;
+typedef Servo hal_servo_t;
#define F_CPU 100000000
#define SystemCoreClock F_CPU
@@ -232,8 +234,10 @@ public:
* No option to invert the duty cycle [default = false]
* No option to change the scale of the provided value to enable finer PWM duty control [default = 255]
*/
- static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) {
- analogWrite(pin, v);
+ static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false) {
+ auto value = map(v, 0, v_size, 0, UINT16_MAX);
+ value = invert ? UINT16_MAX - value : value;
+ analogWrite(pin, value);
}
static void set_pwm_frequency(const pin_t, int) {}
diff --git a/Marlin/src/HAL/NATIVE_SIM/Servo.cpp b/Marlin/src/HAL/NATIVE_SIM/Servo.cpp
new file mode 100644
index 0000000000..a15eda1bf6
--- /dev/null
+++ b/Marlin/src/HAL/NATIVE_SIM/Servo.cpp
@@ -0,0 +1,104 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#include "../platforms.h"
+
+#ifdef __PLAT_NATIVE_SIM__
+
+#include "../../inc/MarlinConfig.h"
+
+#if HAS_SERVOS
+
+#include "Servo.h"
+
+//#define DEBUG_SERVOS
+#define DEBUG_OUT ENABLED(DEBUG_SERVOS)
+#include "../../../core/debug_out.h"
+
+uint8_t ServoCount = 0; // the total number of attached servos
+
+Servo::Servo() {
+ // Constructor stub
+ DEBUG_ECHOLNPGM("Debug Servo: constructor");
+ this->servoIndex = ServoCount++; // assign a servo index to this instance
+}
+
+uint8_t Servo::attach(int pin) {
+ // Attach stub
+ DEBUG_ECHOLNPGM("Debug Servo: attach to pin ", pin, " servo index ", this->servoIndex);
+ return attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
+}
+
+uint8_t Servo::attach(int pin, int min, int max) {
+ // Attach with min and max stub
+ DEBUG_ECHOLNPGM("Debug Servo: attach to pin ", pin, " with min ", min, " and max ", max);
+ if (pin > 0) servo_pin = pin;
+ return this->servoIndex;
+}
+
+void Servo::detach() {
+ // Detach stub
+ DEBUG_ECHOLNPGM("Debug Servo: detach");
+}
+
+// If value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
+void Servo::write(int value) {
+ if (value < MIN_PULSE_WIDTH) { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
+ value = map(constrain(value, 0, 180), 0, 180, SERVO_MIN_US(min), SERVO_MAX_US(max));
+ }
+ writeMicroseconds(value);
+ DEBUG_ECHOLNPGM("Debug Servo: write ", value);
+}
+
+void Servo::writeMicroseconds(int value) {
+ // Simulate the servo movement
+ this->value = value;
+ hal.set_pwm_duty(pin_t(this->servo_pin), (float(value) / 20000) * UINT16_MAX, UINT16_MAX);
+ DEBUG_ECHOLNPGM("Debug Servo: write microseconds ", value);
+}
+
+int Servo::read() {
+ // Read stub
+ DEBUG_ECHOLNPGM("Debug Servo: read ", this->value);
+ return this->value;
+}
+
+int Servo::readMicroseconds() {
+ // Read microseconds stub
+ DEBUG_ECHOLNPGM("Debug Servo: read microseconds");
+ return 0;
+}
+
+bool Servo::attached() {
+ // Attached stub
+ DEBUG_ECHOLNPGM("Debug Servo: attached");
+ return false;
+}
+
+int Servo::move(const unsigned char cmd) {
+ // Move stub
+ DEBUG_ECHOLNPGM("Debug Servo: move ", cmd);
+ write(cmd);
+ return 0;
+}
+
+#endif // HAS_SERVOS
+#endif // __PLAT_NATIVE_SIM__
diff --git a/Marlin/src/HAL/NATIVE_SIM/Servo.h b/Marlin/src/HAL/NATIVE_SIM/Servo.h
new file mode 100644
index 0000000000..428871142c
--- /dev/null
+++ b/Marlin/src/HAL/NATIVE_SIM/Servo.h
@@ -0,0 +1,48 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#pragma once
+
+#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
+#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
+#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
+#define SERVO_MIN_US(v) (MIN_PULSE_WIDTH - (v) * 4) // minimum value in uS for this servo
+#define SERVO_MAX_US(v) (MAX_PULSE_WIDTH - (v) * 4) // maximum value in uS for this servo
+
+class Servo {
+public:
+ Servo();
+ uint8_t attach(int pin); // Attach the given pin to the next free channel, set pinMode, return channel number or INVALID_SERVO if failure
+ uint8_t attach(int pin, int min, int max); // As above but also set min and max values for writes.
+ void detach();
+ void write(int value); // If value is < 200 it's treated as an angle, otherwise as pulse width in microseconds
+ void writeMicroseconds(int value); // Write pulse width in microseconds
+ int read(); // Return current pulse width as an angle between 0 and 180 degrees
+ int readMicroseconds(); // Return current pulse width in microseconds for this servo
+ bool attached(); // Return true if this servo is attached, otherwise false
+ int move (const unsigned char cmd);
+private:
+ uint8_t servoIndex; // Index into the channel data for this servo
+ int8_t min; // Minimum is this value times 4 added to MIN_PULSE_WIDTH
+ int8_t max; // Maximum is this value times 4 added to MAX_PULSE_WIDTH
+ int value; // Pulse width in microseconds for this servo
+ int servo_pin = 0; // pin number for this servo
+};
diff --git a/Marlin/src/HAL/NATIVE_SIM/endstop_interrupts.h b/Marlin/src/HAL/NATIVE_SIM/endstop_interrupts.h
new file mode 100644
index 0000000000..e74c312274
--- /dev/null
+++ b/Marlin/src/HAL/NATIVE_SIM/endstop_interrupts.h
@@ -0,0 +1,30 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#pragma once
+
+#error "ENDSTOP_INTERRUPTS_FEATURE is not supported in this simulation environment."
+
+void setup_endstop_interrupts() {
+ // This function is a stub for setting up endstop interrupts.
+ // Since this is a simulation environment, actual hardware interrupts
+ // are not applicable. Add any necessary simulation-specific logic here.
+}
diff --git a/Marlin/src/HAL/NATIVE_SIM/servo_private.h b/Marlin/src/HAL/NATIVE_SIM/servo_private.h
deleted file mode 100644
index e0eb30ab28..0000000000
--- a/Marlin/src/HAL/NATIVE_SIM/servo_private.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * Marlin 3D Printer Firmware
- * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
- *
- * Based on Sprinter and grbl.
- * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-#pragma once
-
-/**
- * servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
- * Copyright (c) 2009 Michael Margolis. All right reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/**
- * Based on "servo.h - Interrupt driven Servo library for Arduino using 16 bit timers -
- * Version 2 Copyright (c) 2009 Michael Margolis. All right reserved.
- *
- * The only modification was to update/delete macros to match the LPC176x.
- *
- */
-
-#include
-
-// Macros
-//values in microseconds
-#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
-#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
-#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
-#define REFRESH_INTERVAL 20000 // minimum time to refresh servos in microseconds
-
-#define MAX_SERVOS 4
-
-#define INVALID_SERVO 255 // flag indicating an invalid servo index
-
-// Types
-
-typedef struct {
- uint8_t nbr : 8 ; // a pin number from 0 to 254 (255 signals invalid pin)
- uint8_t isActive : 1 ; // true if this channel is enabled, pin not pulsed if false
-} ServoPin_t;
-
-typedef struct {
- ServoPin_t Pin;
- unsigned int pulse_width; // pulse width in microseconds
-} ServoInfo_t;
-
-// Global variables
-
-extern uint8_t ServoCount;
-extern ServoInfo_t servo_info[MAX_SERVOS];
diff --git a/Marlin/src/HAL/shared/servo.h b/Marlin/src/HAL/shared/servo.h
index 0101fcb628..85e645146c 100644
--- a/Marlin/src/HAL/shared/servo.h
+++ b/Marlin/src/HAL/shared/servo.h
@@ -59,7 +59,7 @@
* write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds)
* writeMicroseconds() - Sets the servo pulse width in microseconds
* read() - Gets the last written servo pulse width as an angle between 0 and 180.
- * readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
+ * readMicroseconds() - Gets the last written servo pulse width in microseconds.
* attached() - Returns true if there is a servo attached.
* detach() - Stops an attached servos from pulsing its i/o pin.
* move(angle) - Sequence of attach(0), write(angle),
@@ -86,6 +86,8 @@
#include "../ESP32/Servo.h"
#elif defined(__PLAT_RP2040__)
#include "../RP2040/Servo.h"
+#elif defined(__PLAT_NATIVE_SIM__)
+ #include "../NATIVE_SIM/Servo.h"
#else
#include
@@ -100,22 +102,22 @@
class Servo {
public:
Servo();
- int8_t attach(const int pin); // attach the given pin to the next free channel, set pinMode, return channel number (-1 on fail)
- int8_t attach(const int pin, const int min, const int max); // as above but also sets min and max values for writes.
+ int8_t attach(const int pin); // Attach the given pin to the next free channel, set pinMode, return channel number (-1 on fail)
+ int8_t attach(const int pin, const int min, const int max); // As above but also set min and max values for writes.
void detach();
- void write(int value); // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
- void writeMicroseconds(int value); // write pulse width in microseconds
- void move(const int value); // attach the servo, then move to value
- // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
- // if DEACTIVATE_SERVOS_AFTER_MOVE wait SERVO_DELAY, then detach
- int read(); // returns current pulse width as an angle between 0 and 180 degrees
- int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
- bool attached(); // return true if this servo is attached, otherwise false
+ void write(int value); // If value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
+ void writeMicroseconds(int value); // Write pulse width in microseconds
+ void move(const int value); // Attach the servo, then move to value
+ // If value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
+ // If DEACTIVATE_SERVOS_AFTER_MOVE wait SERVO_DELAY, then detach
+ int read(); // Return current pulse width as an angle between 0 and 180 degrees
+ int readMicroseconds(); // Return current pulse width in microseconds for this servo
+ bool attached(); // Return true if this servo is attached, otherwise false
private:
- uint8_t servoIndex; // index into the channel data for this servo
- int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
- int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
+ uint8_t servoIndex; // Index into the channel data for this servo
+ int8_t min; // Minimum is this value times 4 added to MIN_PULSE_WIDTH
+ int8_t max; // Maximum is this value times 4 added to MAX_PULSE_WIDTH
};
#endif
diff --git a/ini/native.ini b/ini/native.ini
index 479155a78c..f01e72ca67 100644
--- a/ini/native.ini
+++ b/ini/native.ini
@@ -58,7 +58,7 @@ debug_build_flags = -fstack-protector-strong -g -g3 -ggdb
lib_compat_mode = off
build_src_filter = ${common.default_src_filter} +
lib_deps = ${common.lib_deps}
- MarlinSimUI=https://github.com/p3p/MarlinSimUI/archive/afe7c1c293.zip
+ MarlinSimUI=https://github.com/p3p/MarlinSimUI/archive/6ea016e104.zip
Adafruit NeoPixel=https://github.com/p3p/Adafruit_NeoPixel/archive/c6b319f447.zip
LiquidCrystal=https://github.com/p3p/LiquidCrystal/archive/322fb5fc23.zip
extra_scripts = ${common.extra_scripts}