A known issue with the common CNC6040 router and similar devices is very poor calibration / linearity of the spindle motor response to gcode Sx commands.

Above is the system block diagram. The grbl_ESP32 gcode interpeter processes a gcode S (speed) command, converting it to a variable duty cycle PWM waveform on parallel port pin 1.

## Diagnosing the problem

The first step in that path is quite linear, the graph above shows the relationship between measured PWM duty cycle and requested speed (Sx).

## new grbl_ESP32 spindle speed linearisation facility

grbl has recently had a facility added for piecewise linearisation of the spindle curve.

This has now been ported to grbl_ESP32 and committed to the master branch by b7ee4265515757c82a732c4b19e3a976ffc39a56 on 20191014.

The documented method is to set grbl_ESP32 up so that spindle speed is configured for a range of 1 to 255 (forcing a 1:1 correspondence between input and PWM value), and to record measured spindle speed for a range of input values.

In my case, I created a gcode file to step through a set of values and dwell for 5s, and the spindle speed was measured with an optical tachometer (UT372) and the observations written to a file.

Above is a plot of measured spindle speed vs PWM value.

The next step is to load those values into a supplied Python scripts which finds a piecewise compensation approximation.

Above is the graphic summary of a 3 section fit.

The script supplies a bunch of program constants that have to be loaded to implement the compensation.

D:\src\Grbl_Esp32\fit>fit_nonlinear_spindle.py CONFIG: N_pieces: 3 PWM_min: 4.0 PWM_max: 196.0 PWM_point1: 8.0 PWM_point2: 12.0 N_data: 46 PWM_set: [ 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 22. 24. 26. 28. 30. 35. 40. 45. 50. 55. 60. 65. 70. 75. 80. 90. 100. 110. 120. 130. 140. 150. 160. 170. 180. 190. 192. 194. 196.] RPM_measured: [ 1423. 2231. 2915. 3481. 3941. 4309. 4591. 4827. 5017. 5204. 5371. 5512. 5651. 5771. 5912. 6031. 6138. 6359. 6572. 6763. 6972. 7184. 7705. 8176. 8671. 9167. 9660. 10167. 10651. 11184. 11690. 12182. 13196. 14210. 15233. 16262. 17324. 18359. 19396. 20429. 21517. 22554. 23685. 23922. 23945. 23944.] SOLUTION: [Update these #define values and uncomment] [ENABLE_PIECEWISE_LINEAR_SPINDLE in config.h.] #define N_PIECES 3 #define RPM_MAX 24149.0 #define RPM_MIN 1552.7 #define RPM_POINT12 4020.2 #define RPM_POINT23 5240.1 #define RPM_LINE_A1 1.621130e-03 #define RPM_LINE_B1 -1.482797e+00 #define RPM_LINE_A2 3.278733e-03 #define RPM_LINE_B2 5.181036e+00 #define RPM_LINE_A3 9.730872e-03 #define RPM_LINE_B3 3.899117e+01 [To operate over full model range, manually write these] ['$' settings or alter values in defaults.h. Grbl will] [operate between min($30,RPM_MAX) and max($31,RPM_MIN)] $30=24149.0 (rpm max) $31=1552.7 (rpm min) [Update the following #define values in cpu_map.h] #define SPINDLE_PWM_MIN_VALUE 4 #define SPINDLE_PWM_MAX_VALUE 196 [1552.7462823692342, 4020.1611749474096, 5240.144531140442, 24149.036632505395] [1552.74628237 616.85372314 304.99583905 102.76571794] [[ 4.71702537e+03 -1.44931297e+03 2.79049481e+02 -2.87569907e-01] [-1.44931297e+03 6.32384871e+02 -2.79049450e+02 2.87570282e-01] [ 2.79049481e+02 -2.79049450e+02 2.43459738e+02 -1.09276718e+00] [-2.87569907e-01 2.87570282e-01 -1.09276718e+00 5.40151890e-02]] [68.68060403 25.14726369 15.60319641 0.23241168]

Above is the output of fit_nonlinear_spindle.py (some additions by me at the end to examine the quality of the fit).

I tweaked some coefficients in fit_nonlinear_spindle.py to improve the model on my data.

PWM_point1 = 8.0 # (S) Point between segments 0 and 1. Used when n_pieces >= 2. PWM_point2 = 12.0 # (S) Point between segments 1 and 2. Used when n_pieces >= 3. PWM_point3 = 100.0 # (S) Point between segments 2 and 3. Used when n_pieces = 4.

## Implementation

The code changes were implemented and tested.

#define ENABLE_PIECEWISE_LINEAR_SPINDLE // Default disabled. Uncomment to enable. #define N_PIECES 3 #define RPM_MAX 24149.0 #define RPM_MIN 1552.7 #define RPM_POINT12 4020.2 #define RPM_POINT23 5240.1 #define RPM_LINE_A1 1.621130e-03 #define RPM_LINE_B1 -1.482797e+00 #define RPM_LINE_A2 3.278733e-03 #define RPM_LINE_B2 5.181036e+00 #define RPM_LINE_A3 9.730872e-03 #define RPM_LINE_B3 3.899117e+01

Above, the fitting constants applied to config.h.

Above, calibration is much more accurate. The blue line has a slope that is very close to 1, and a Y intercept that is very close to zero.

Note that the calibration coefficients used here are specific to the instance, and appropriate coefficients for other instances must be established by measurement as described.

Bart Dring is a responsive developer with whom it was a pleasure to collaborate.