- M5 now disables PWM completely in both standard and servo modes - Inverted servo mapping: S0=pen up (16 ticks), S1000=pen down (31 ticks) - Updated SERVO_MODE.md documentation to reflect new behavior - Changed config defaults: MIN_PULSE=16, MAX_PULSE=31
6.0 KiB
GRBL Servo Mode for Spindle PWM
This modification adds servo control capability to GRBL's spindle PWM output, allowing you to control hobby servos instead of traditional spindles. This is useful for applications like:
- Pen plotters (pen up/down)
- Drag knives
- Laser focusing mechanisms
- Any application requiring servo positioning
How It Works
Standard Spindle Mode (Default)
- Frequency: ~7.8kHz (1/8 prescaler)
- Duty Cycle: 0-255 (full range)
- Use Case: Variable speed spindle control
Servo Mode (When Enabled)
- Frequency: ~61Hz (1/1024 prescaler)
- Target: 50Hz (20ms period) for servos
- Actual: 61Hz works well with most hobby servos
- Duty Cycle: 16-31 ticks (~1-2ms pulses)
- 16 ticks ≈ 1.024ms (0° position)
- 31 ticks ≈ 1.984ms (180° position)
- Use Case: Servo position control
Configuration
Enable Servo Mode
In config.h, uncomment:
#define USE_SPINDLE_SERVO_MODE
Adjust Servo Pulse Range (Optional)
If your servo requires different pulse widths, adjust in config.h:
#define SPINDLE_SERVO_MIN_PULSE 16 // S0 position (pen UP) - ~1ms pulse
#define SPINDLE_SERVO_MAX_PULSE 31 // S1000 position (pen DOWN) - ~2ms pulse
Note:
- Each tick = 64μs at 1/1024 prescaler on 16MHz Arduino
- MIN_PULSE is used for S0 (pen up), MAX_PULSE is used for S1000 (pen down)
- If your servo moves in the opposite direction, swap these values
Set RPM Range
Configure the RPM range in config.h to map to servo positions:
#define SPINDLE_MAX_RPM 1000.0 // Maps to max servo position (180°)
#define SPINDLE_MIN_RPM 0.0 // Maps to min servo position (0°)
Usage
Send G-code commands to control servo position:
M3 S0 ; Pen UP (uses SPINDLE_SERVO_MIN_PULSE = 16 ticks / ~1ms)
M3 S500 ; Mid position
M3 S1000 ; Pen DOWN (uses SPINDLE_SERVO_MAX_PULSE = 31 ticks / ~2ms)
M5 ; Disable PWM (servo unpowered)
Note: In servo mode, M5 disables the PWM output completely, which unpowers the servo. Use M3 S0 to keep the servo powered at the minimum position (pen up).
The S value is linearly mapped (inverted):
- M3 S0 → Uses SPINDLE_SERVO_MIN_PULSE (pen up / 16 ticks / ~1ms)
- M3 S1000 → Uses SPINDLE_SERVO_MAX_PULSE (pen down / 31 ticks / ~2ms)
- M5 → Disables PWM (servo unpowered)
- Values in between are linearly interpolated
Technical Details
Timer Configuration
Standard Mode:
TCCRB_REGISTER = 0x02 // CS21=1: 1/8 prescaler
Frequency = 16MHz / (8 * 256) = 7,812.5 Hz
Servo Mode:
TCCRB_REGISTER = 0x07 // CS22=1, CS21=1, CS20=1: 1/1024 prescaler
Frequency = 16MHz / (1024 * 256) = 61.04 Hz (period ≈ 16.38ms)
PWM Calculation
Standard Mode:
current_pwm = floor(rpm * (255 / RPM_RANGE) + 0.5)
Range: 0-255
Servo Mode (Inverted):
inverted_rpm = MAX_RPM - rpm
current_pwm = floor(inverted_rpm * (15 / RPM_RANGE) + 16 + 0.5)
Range: 16-31 (15 discrete positions)
S0 → 16 (pen up), S1000 → 31 (pen down)
Resolution
With the default configuration:
- Servo pulse range: 1.024ms - 1.984ms (960μs range)
- Step size: 64μs per tick
- Positions: 16 discrete positions (15 steps)
This provides sufficient resolution for typical pen plotter and similar applications where precise servo positioning is not critical.
Hardware Connection
- Connect servo signal wire to Arduino Pin D11 (PWM output)
- Connect servo power (5V) to appropriate power source
- Connect servo ground to Arduino/power source ground
Warning: Most servos draw more current than Arduino can provide. Use external 5V power supply with common ground.
Compatibility
- ✅ Arduino Uno (ATmega328p) - Fully supported
- ❌ Arduino Mega (ATmega2560) - Not supported (uses Timer4)
- ❌ Other processors - Not tested
References
Based on the excellent work by Bart Dring:
- Blog post: Using Grbl's Spindle PWM to Control a Servo
- GitHub repo: Grbl_Pen_Servo
Implementation Files
config.h- Configuration optionsspindle_control.c- Servo mode logiccpu_map/cpu_map_atmega328p.h- Timer register definitions
Troubleshooting
Servo jitters or doesn't move smoothly
- Check power supply - servos need stable 5V
- Verify pulse width range matches your servo specs
- Some servos may require different MIN/MAX pulse values
Servo moves to wrong positions
- Adjust
SPINDLE_SERVO_MIN_PULSEandSPINDLE_SERVO_MAX_PULSE - Check that
SPINDLE_MIN_RPMandSPINDLE_MAX_RPMare set correctly - Verify S values in G-code are within MIN/MAX RPM range
Servo doesn't respond at all
- Verify
VARIABLE_SPINDLEis enabled - Check
USE_SPINDLE_SERVO_MODEis uncommented - Confirm connection to Pin D11
- Test PWM output with logic analyzer or oscilloscope
Example: Pen Plotter
// config.h settings for pen plotter
#define VARIABLE_SPINDLE
#define USE_SPINDLE_SERVO_MODE
#define SPINDLE_MAX_RPM 1000.0
#define SPINDLE_MIN_RPM 0.0
#define SPINDLE_SERVO_MIN_PULSE 16 // S0 = Pen UP (~1ms pulse)
#define SPINDLE_SERVO_MAX_PULSE 31 // S1000 = Pen DOWN (~2ms pulse)
; Pen up (keeping servo powered)
M3 S0
; Move to start position
G0 X10 Y10
; Pen down
M3 S1000
; Draw square
G1 X20 Y10 F1000
G1 X20 Y20
G1 X10 Y20
G1 X10 Y10
; Pen up (keeping servo powered)
M3 S0
Pen Plotter Commands:
- M3 S0 = Pen UP (16 ticks / ~1ms / MIN_PULSE - servo powered)
- M3 S1000 = Pen DOWN (31 ticks / ~2ms / MAX_PULSE - servo powered)
- M5 = Disable PWM (servo unpowered)
Servo Direction:
- Default configuration: S0 = pen up (MIN_PULSE), S1000 = pen down (MAX_PULSE)
- If your servo moves backwards, swap the MIN and MAX pulse values
- Fine-tune the exact pulse values (16-31 range) to match your servo's physical limits