Files
grbl/SERVO_MODE.md
devdesk 67a98b78b4 Invert servo PWM mapping and fix M5 behavior
- 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
2025-12-04 14:42:17 +02:00

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

  1. Connect servo signal wire to Arduino Pin D11 (PWM output)
  2. Connect servo power (5V) to appropriate power source
  3. 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:

Implementation Files

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_PULSE and SPINDLE_SERVO_MAX_PULSE
  • Check that SPINDLE_MIN_RPM and SPINDLE_MAX_RPM are set correctly
  • Verify S values in G-code are within MIN/MAX RPM range

Servo doesn't respond at all

  • Verify VARIABLE_SPINDLE is enabled
  • Check USE_SPINDLE_SERVO_MODE is 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