@dc42 I have a suggestion for making things much simpler and not further complicating RRF GCODE.
We basically need M3, M4 and M5 commands to handle the spindle at the GCODE level, plus M950 to init the corresponding io.
As far as I understand, these commands, handle a spindle created with M950, which, in turn make the assumption we drive the spindle by pwm through a given pin. Nothing is related to modbus nor serial link in M950. This command is somewhat complex, because you handle many different peripherals (led strip,PWM, servo, temp, etc), everything that can be handled by DUET standard io at the hardware level.
As you said, there is little chance that you can support each and every modbus motor/controller on the market, and this should be the case, I think the program memory should be filled by unused code.
On the other hand, a GCODE command not implemented in RRF will call a macro of the same name ( e.g. M1000.g)
We could use a M3.1 M4.1 M5.1 to handle the modbus specific, but this would not be standard and would need some post processing.
I suggest that RRF, for limited control codes (M3,M4,M5...), instead of handling what is defined by M950, just call corresponding macros when instructed by M950.
This way, M3,M4,M5 would be handled by the user, it's the user's responsibility to handle all the modbus specifics inside these macros.
Introduce a parameter M (0 or 1, default 0) in M950 that tells, for the corresponding spindle (R parameter), to call M3_R.g, M4_R.g, M5_R.g R being the spindle number. To avoid a race condition, M3,M4,M5 commands called from inside these macros should not be possible, either raise an error or just do nothing.
modbus complexity would then be handled by a macro.
you can extend this to other devices (fans, heater, leds,....) managed through serial, spi or I2C devices.
M3 P1 S1000 would call the macro M3_1.g with parameter S=1000 exactly like M98 P"M3_1.g" S1000.
M950 with the M1 and R1 would also call a macro M950_1.g that would init the serial link, maybe check the modbus status to make sure it will work.
You could expand this to all codes related to M950 for handling special cases not handled by DUET ios.
I think it would solve the issues I have seen on the forum, included mine without introducing specific GCODES.