FluidNC supports multiple spindles on one machine. Spindles can be controlled by different hardware interfaces like relays, PWM, DACs, or RS485 serial interfaces to VFDs. Lasers are treated as spindles.
Each spindle is assigned a range of tool numbers. You change spindles by issuing the GCode command "M6 Tn", where n is the tool number. Tool numbers within the assigned range for a given spindle will activate that spindle - and the detailed number within the range could be used to select the specific tool on the spindle. This lets you have, for example, a single machine with an ATC spindle and a laser. A single GCode file could allow you to etch and cut out a part. Most CAM programs support tool numbers. You could also have a gantry with both a low-speed high-torque pulley spindle and also a high-speed direct drive spindle.

These are on a separate wiki page.
0-10V control is designed for spindle controllers that have a 0-10V control input as well as separate pins for forward and reverse direction. The ESP32 cannot directly generate a 0 to 10V signal, but some FluidNC controllers have an adapter circuit that generates a 0 to 10V analog voltage from an ESP32 GPIO that is pulsed with a pulse-width modulation (PWM) waveform. The basic PWM spindle type can also be used with such hardware adapters, but it does not support separate forward and reverse direction pins. If you don't need that style of direction control, you can use the PWM spindle type.
Example
10V:
forward_pin: gpio.13
reverse_pin: gpio.17
pwm_hz: 5000
output_pin: gpio.4
enable_pin: NO_PIN
direction_pin: NO_PIN
disable_with_s0: false
s0_with_disable: true
spinup_ms: 0
spindown_ms: 0
tool_num: 0
speed_map: 0=0.000% 1000=0.000% 24000=100.000%
off_on_alarm: false
The DAC uses the ESP32's built in DAC hardware. This can only be used on gpio.25 and gpio.26. It outputs a 0-3.3V analog voltage (not PWM). In most cases a PWM will be better. The DAC resolution is only 8 bit (0-255) and a PWM can be up to 16 bit (0-65535).
example
DAC:
output_pin: gpio.25
enable_pin: NO_PIN
direction_pin: NO_PIN
disable_with_s0: false
s0_with_disable: true
spinup_ms: 0
spindown_ms: 0
tool_num: 100
speed_map: 0=0.000% 255=100.000%
off_on_alarm: false
The M4 (spindle reverse on) command will only be accepted if a direction pin is assigned to an I/O pin.
pwm:
pwm_hz: 5000
direction_pin: NO_PIN
output_pin: gpio.14
enable_pin: NO_PIN
disable_with_s0: false
s0_with_disable: true
spinup_ms: 0
spindown_ms: 0
tool_num: 0
speed_map: 0=0.000% 10000=100.000%
off_on_alarm: false
BESC means "Brushless Electronic Speed Controller" of the type used to power propeller motors for hobby-type radio-controlled planes, helicopters, and drones. Those motors can be used for high-speed spindles on light-duty machines that do not have substantial tool side loads. They use the same type of PWM signal as an RC servo. Conventional PWM controls power by adjusting the duty cycle between 0% and 100%, whereas RC servo PWM adjusts the pulse length between (typically) 1 ms (for motor off) and 2 ms (motor full on) within a pulse repetition period of about 20 ms. Only one PWM-capable I/O pin is required. It must be a digital output pin that presents the raw PWM waveform, not a PWM-to-analog output that creates a variable DC voltage by low-pass filtering the PWM waveform.
Earlier versions of FluidNC had a special BESC spindle type, but we realized that, with suitable config settings for pwm_hz and speed_map, the PWM spindle type works perfectly for BESCs.
The usual pulse repetition rate for BESCs is 20ms which is 50 Hz in frequency units, so set the pwm_hz config item to 50 (some BESCs can operate with higher pulse repetition rates, up to perhaps 200Hz). Let's assume that the minimum pulse time is the typical 1ms (motor off) and the maximum time is 2 ms (motor full on). 1ms is 5% of 20ms and 2ms is 10%. Let's also assume that you wish to set the motor speed with GCode S values between 0 and 1000. Therefore, the speed_map config item would have the value "0=5% 1000=10%".
pwm:
output_pin: gpio.4
pwm_hz: 50
speed_map: 0=5% 1000=10%
You can fine-tune those values as needed for your specific hardware. If you want to use S values in units of RPM, and your motor runs at 20000 RPM at full power, just replace "1000" with "20000" in speed_map. Most hobby RC motors do not have speed sensors so their speed control is not precise; the RPM value would just be an approximation.
The config settings shown above are the ones that are absolutely necessary for a BESC. You can set other PWM config items for things like spinup and spindown delays.
This could also be used to control a hobby servo in an application like a pen plotter. With this setup you could move the pen down with the GCode
M3 S1000and lift it withM5orM3 S0.
Also see the RC servo feature under motors axes.
Hobby BESCs often have a "programming mode" that can be entered by powering up the BESC with the radio control transmitter's throttle stick in specific positions, then moving the throttle to other positions after hearing beep patterns from the BESC. It is sometimes possible to do that from GCode, using commands like "M3 S0" for minimum throttle, "M3 S1000" for full throttle, and "M3 S500" for mid-throttle. Typically you would issue the first M3 command for the initial throttle position with the BESC powered off, then power it on and go through the specified sequence as the BESC responds with beeps or LED flashes.
This is like a PWM spindle except that you have separate PWM signals for clockwise (CW) and counterclockwise (CCW) rotation. This was specifically designed to directly control a H bridge circuit.
HBridge:
pwm_hz: 5000
output_cw_pin: gpio.4
output_ccw_pin: gpio.16
enable_pin: gpio.26
disable_with_s0: false
spinup_ms: 1000
spindown_ms: 1000
tool_num: 100
speed_map: 0=0.000% 10000=100.000%
off_on_alarm: false
A laser is considered a spindle because gcode does not have laser specific codes. It uses the RPM value as a power level. Lasers also have special requirements.
They always operate like the advanced laser mode of Grbl
speed_map: final xxx=100% can be whatever you want, but it is typically 255 or 1000. This would need to be used in the CAM software as the max power number.
off_on_alarm: true: is recommended from a safety point of view to ensure the laser is switched off when movement is stopped due to triggered alarm.
The 2 modes are quite different and each optimized for different types of work.
M3 Mode
This mode is primarily used for cutting through parts. The laser operates whenever you are in a feed rate controller mode (G1, G2 or G3). It will stay on at all times at the full Snnn value. This includes when there is no motion. To stop the laser you must send M5, G0 or S0. This gives you full control. For example, you may want to dwell a fraction of a second at the start or end of a cut.
Here is an example of a macro to test the laser at minimal power
M3 S1 ; lowest power
G1 F100 ; set G1 and an arbitrary feedrate to turn on the laser
G4 P0.50 ; wait 0.5 seconds
G0 ; turn off the laser
M5 ; keep it off.
M4 Mode
M4 mode is primarily used for engraving. It compensates to lower the power of the laser during acceleration and deceleration to prevent darkening those sections. It will stay off when there is no motion.
Config example
Laser:
pwm_hz: 5000
output_pin: gpio.4
enable_pin: NO_PIN
disable_with_s0: false
s0_with_disable: true
tool_num: 0
speed_map: 0=0.000% 255=100.000%
off_on_alarm: true
This is like a PWM signal except that the pin will be full on for any speed above 0 that you select. PWM signals can quickly destroy a relay.
relay:
direction_pin: NO_PIN
output_pin: gpio.26
enable_pin: NO_PIN
disable_with_s0: false
s0_with_disable: true
spinup_ms: 0
spindown_ms: 0
tool_num: 0
speed_map: 0=0.000% 0=100.000% 1=100.000%
off_on_alarm: false
This is a default spindle that is automatically created if you did not specify a spindle in your config file.
NoSpindle:
You can define as many spindles as your hardware will support. They will act independently. You must use separate I/O pins for each spindle. Simply add each spindle definition to the config file.
The active spindle is determined by the active tool number, each spindle must have its own range of tool numbers. The spindle tool_num: config file item determines the first tool in the spindle's range. The spindle's tool number range goes until the next defined spindles tool_num: config item. One of your spindle's tool_num: must be 0 to insure all tool numbers are valid.
If you don't follow the tool numbering rules, you will get warnings and tool numbers will be temporarily assigned. The tool numbers will be assigned in the order that the spindles appear in the config file and have a range of 100.
You change tools with the gcodes T<num> M6 . The T value sets the next active tool and M6 makes the actual change. You can see the current T value by sending $G to get all the current modal values.
$G
[GC:G0 G54 G17 G21 G90 G94 M5 M9 T2 F0 S12000]
Most people should put the the
T<num>andM6on the same gcode line. If you sendT<num>without an M6, the current T value will be set, but the tool change will not happen. $G will report the T value, but the spindle will still be using previous value. Some advanced ATC machines could use this feature to get the next tool ready while running the previous tool.
Here is what happens when the M6 command is received
If you have 2 spindles of the same type, like 2 PWM spindles. They will have the same name in the config file. That is fine, but there is no way to access the second spindle with $ commands. It will always respond with the first spindle data. $pwm/output_pin will respond $/pwm/output_pin=gpio.14 in the example below. $CD will show both spindles.
PWM:
pwm_hz: 5000
direction_pin: NO_PIN
output_pin: gpio.14
enable_pin: gpio.13
disable_with_s0: false
spinup_ms: 0
spindown_ms: 0
tool_num: 0
speed_map: 0=0.00% 10000=100.00%
off_on_alarm: false
atc: atc_manual
m6_macro:
s0_with_disable: true
PWM:
pwm_hz: 5000
direction_pin: NO_PIN
output_pin: gpio.15
enable_pin: gpio.12
disable_with_s0: false
spinup_ms: 0
spindown_ms: 0
tool_num: 10
speed_map: 0=0.00% 10000=100.00%
off_on_alarm: false
atc:
m6_macro:
s0_with_disable: true