1
0
mirror of https://github.com/bdring/Grbl_Esp32.git synced 2025-08-28 08:39:51 +02:00

Devt merge to Master (#487)

* Fixing raster_test.nc

- Grbl does not like the feedrate without a G1...should look at the rules here.

* Settings (#408)

* Settings WIP

* Fixed a ton of compilation errors

* Use const char * for object names

In hopes that it is more space efficient than string

* Remove number aliases for extended settings

* Settings WIP

* Fixed a ton of compilation errors

* Use const char * for object names

In hopes that it is more space efficient than string

* Remove number aliases for extended settings

* Settings WIP - factor into files

* Compiles now

* Checkpoint sort of working

* Move grbl names into Settings objects

* Reports working better

* GRBL order for settings display

* platformio debug flags

* Removed ifdef'ed dead code

* More dead code removal

* Removed settings struct

* Fixed bugs with enums and checks,
added backspace handling, fixed upper casing
and space trimming.

* Fewer files part 1

* Fewer settings files part 2

* Get rid of problem reports from Intellisense

* WIP on WebUI interaction

* WebCommands are working

* GrblCommands not dollarCommands

* Split Commands and Settings

* Working with WebUI

* Remove prefs references and redundant #defines

* Dead code removal

* Fixed settings names

* Cleaned out dead code and fixed ENABLE_WIFI off

* Fixed acceleration units.

* Removed Notes file, disabled private debug .ini

* accel defaults in seconds not minutes

* Update accel defaults

* Fixed key length problem, added $ help

* Fixed read and store coord data

- changed MAX_N_AXIS to N_AXIS

* Respect config options for enabling some commands

* Fixed "const" problem in last commit

* startup lines, build info, idle state fixes

* Cleanup TODOs and mark some as Settings related

* Idle state for sets, canonical gcode for startup lines

* Fixed botched IDLE or ALARM test

* Fixed motor diable with sleep $SLP

- Motors were not disabling with sleep. This was probably a problem for a while

* Don't list settings while running

* Added jog, with gcode preprocessing for $Nn and $J

* Removed dead code

* More dead code removed

* Compiles on Arduino IDE
Got rid of c++11 features and std::string's

* Deleted a .ino.cpp that snuck into a commit

* Fixed IP address defaults

* Fixed memory leak that was causing crashes

* Fixed web settings string length check limits

* Implemented ESP / ESP0 web help

* Added error list $e / $e=nn

* Fixed webui setting of enumerated types

Co-authored-by: bdring <barton.dring@gmail.com>

* Fix compile with SD card not enabled.

* Partial match of settings names (#419)

* Partial match of settings names

* Removed a dead line

* Fix compile problems with different ENABLE_s

Some WebUI commands were present even when the
relevant ENABLE_ was not defined,  This corrects
that and make the presence/absense of the various
commands correspond to the old code with different
combinations of web-related enables in config.h

To make things easier to find, the order of the
web command functions in the file now corresponds
with the listed order of the command names.

* Fixed instability in changing settings

* Linux fix

* Fixed #430

* Bump build date to reflect change to lowrider_v1p2.h

* Fix #431

Added INVERT_SPINDLE_OUTPUT_PIN option

* Bump build date

* I2s (#448)

* Travis CI build test covers all machine config

* Just copied from Marlin (first commit)

* First ported to Grbl_Esp32 (not yet working)

* Compile i2s only if USE_I2S_EXPANDER is defined.

* rename defines and filenames for I2S I/O expander

* Pulse generator has been implemented

* Fix I2S hander

* remove double lock from i2s_reset_fifo

and make file static functions to static

* Implement stepper stream callbacks

* Fix some bit manipulation bugs

* make function name unique

* make define name unique

* define a base number of extended pins

* machine configuration for the "esp32 printer controller"

* Simplify stepper callbacks

* Cleanup & add some detail comments, stepper bug fix

bugfix
- stepper timer woken up unfortunately
- pulse function should push at least one sample in normal

* Enable the 32-bits stepper streamer

* Faster push samples, comment fixed

* Small comment fix

* Finally, the stepper worked properly

- I2S format corrected
- Fixed an indefinite buffer data being used at idle
- Respond to stop and start requests for steppers

* Code has been reverted to always output a bitstream

* Fix buffer clear method

Fixed a bug in clearing the buffer.
- Fixed a problem in which the lower 8 bits of data were used repeatedly by mistake due to a code error, although originally 32 bits of data should be set.
The order of initialization is changed.
- I first enabled the I2S I/O expander and then the I2S Stepper.
-  The I2S pins are set at the beginning of the init function
-  Enable the I2S ISR after the expander task has been created

Removed code about unused push buffers.

* Implement exclusive access to the internal GPIO variable

and the pulse period parameter is limited to 32 bits (due to the computational speed).

* Code cleanup

- Renamed the function to represent the actual behavior
- I fixed an explanation in the comments that was wrong

* Renamed the function to represent the actual behavior

* Move I/O macros to i2s_ioexpander.h

Removed fastio.h, which affects the entire Grbl source.

* raised the resolution of the stepper pulse

I raised the minimum resolution of the stepper pulse from 4us to 2us.
The buffer size and number of buffers were adjusted.

* Try to implement the I2S reset mechanism(not yet works)

I started implementing the reset process for the I2S I/O expander.
It's still not enough.

* Fix unintended 0 data when I2S restart

* Comments modified to match actual behavior

* pulse clock back to the same speed as the original

* Revert "Merge remote-tracking branch 'upstream/Devt' into i2s_io_expander"

This reverts commit 5f57189f1a, reversing
changes made to c4d827c7a8.

* Revert "Revert "Merge remote-tracking branch 'upstream/Devt' into i2s_io_expander""

This reverts commit ab79f4a59a.

* Fix Guru meditation error

The internal state of the i2s should start with the PAUSED state.

Add exclusive handling in the pulser state check and state change.

* Change function names and internal state names

Based on Mitch-san's suggestion.
https://github.com/bdring/Grbl_Esp32/pull/372#issuecomment-620303937

* Clearly initialize the Pulser state

It's not necessary, but it's easy to understand.
Also, if the enum value changes in the future, the problem is not placed.

* Delete Grbl_Esp32.ino.cpp

* Prevents unintended generation of 0 data (Again)

Fixing the code generated unintended 0 data depending on the timing.
I decided to monitor the arrival status of FIFO data and control conf.tx_stop_en to ensure the generation of zero data regardless of the timing.

* Clean up

Modified to use the correct union as a descriptor queue link.

* Clearing the DMA buffer with a dedicated function

I made sure I didn't write the same code twice.

* Fix some comments

* Cleanup comments

* Test configuration for ESP32 Printer Controller

* Homing support

Move the I2S reset function call from mc_reset() to st_go_idle() to stop motors immediately.

* Update configuration for the test board

Settings for combination testing with servo axes.
- Z servo axis is connected to GPIO15

* Prevents unintended generation of 0 data (change implimentation)

Since the synchronization process by status monitoring does not work as intended, I decided to deal with it by simply waiting for a fixed time.

* Fix about unsigned integer

Fixed a problem with using signed values for unsigned integers.

* Define pin numbers for tne I2S expander

* Update esp32_printer_controller.h

* Fix typo and suppress warning at grbl_trinamic.cpp

* Support the 6axis test board

* Fix the data in the left channel to 0

* Reconfig for JTAG debugging

* Realtime I2S I/O Expander (Quick hack)

Undef "I2S_STEPPER_STREAM" if you intend to use the real-time expander control method.

* Fix I2S reset timing

Fixed an issue where the buffer output was reset at an inappropriate timing.

* Move some definitions to the header to compute the delay

* Prevent overrun when enabled the HardLimit feature

I2S_STEPPER_STREAM only

* In I2S real time mode, FIFO, DMA, ISR and Task are not used

Now, unnecessary code is not executed.

* A paranoid fix for deterring I2S DMA/ISR processing

* Tweaks during PCB testing

- Added some test code
- Fixed B and C step generation.
- Made StepStick and Trinamic versions of machine def file

* Update spi_shift_reg_6axis_xyzabc.h

had wrong pin for !RESET

* Some changes that helped

* Fix comments about I2S channel

* Moved some stuff out of stepper.cpp

I moved some stuff out of stepper.cpp using the existing the global MotorClass interface. They will need to be changed when actual objects are used, but the stepper.cpp file will stay the same.

* More code moving

The goal is to make it easier to merge with MotorClass sometime in the future.

* More code reduction in stepper.cpp plus testing on non SR machine.

- moved I2S_IOEXP_PIN_BASE to config.h
- change axis enable pins to disable pins for consistancy
- Fixed after testing on non SR board with and without RMT.

* Merged in some more motor class stuff

- Uses StandardStepperClass for all motors now to do step, dir and enable.  The Trinamic code is still the old way until the CS is figured out.

* Reviving the lost defines for dual motor

* Fixed a variable name about disable/enable the stepper

* Removing an unused prototype

* Use HAL_digitalWrite instead of I2S_IOEXP_WRITE

* Add HAL_digitalRead()

* Using I2S functions instead of I2S macros

* Remove I2S macros and add definition of bit depth for I2S expander

* Make the definition of I2S_IOEXP_NUM_BITS non-mandatory

If the definition of I2S_IOEXP_NUM_BITS is omitted, 32 is used.

* Check if STEPPER MS has been defined.

* Removed the rounding up of unnecessary pulse width values.

* I2S pin number conversion is done using macros.

* Supports 16-bit shift register (2 chains) configuration.

* Cleaning up ganged axes

- #define USE_GANGED_AXES was not needed. That removed some code.
- ABC axes did not have code for ganged axes...added it.
- Created a test machine def for XXYYCC machine.

* Added squaring of AB&C axes

* Delete Grbl_Esp32.ino.cpp

* Rename Machines files for SR test board

* Fixed a compilation error when not using WiFi.

* Eliminate the error of not finding the required definition

* Fix typo

* Remove an obsoleted function to disable steppers

Use motors_set_disable() instead of set_stepper_disable()

* Update GCodePreprocessor.cpp

* Correspondence to the Setting class

* Quick fix for servo axis

We are in the process of migrating to RcServoClass, so I'm making minimal modifications to make sure it compiles successfully.

* Suppresses compile errors when WIFI is disabled.

* Updated the MotorClass and RcServo Class

* Fix typo in MotorClass

* Fix missing endif in WebSettings

* Fix complile error in WebSetting

* Update espresponse.h

* Fixed a compile error in case-sensitive environments

* Update config.h

* Update debug.ini

Added a configuration skeleton for debugging on ESP-Prog and macOS/Linux as a comment.

* Rename i2s_ioexpander.[cpp|h] to i2s_out.[cpp.|h]

* I2S-related functions and variables changed to be based on "i2s out".

* I2S-related defines changed to be based on "i2s out".

* Change the I2S out task name

* Changed the name of the definition for enabling the I2S OUT feature

* Avoid recursive calls when called  from overridden digitalWrite()

* Fix typo in comment

* Overload digitalWrite() etc (by Mitch Bradley)

Catch up with Mitch-san's Pins branch.
387fc14ea8

* Update i2s_out_xyzabc_tmc to use realtime I2S out by default

* Add IRAM_ATTR attribute to new Arduino-compatible functions.

* Make sure to use the latest version of TMCStepper

* Changed back to using I2S stream mode in i2s_out_xyzabc_tmc.

* Override the CS switch function of the TMCStpper to insert a short delay.

* Clean up

* Fix pinName() overwrite

* "I2S Steps" message when it starts up

I have changed the "I2S Steps" message on startup If the I2S streamer is used.

* Working on Trinamic Driver class

- Trying to make Trinamic driver class work with I2S
- grbl_trinamic.cpp works - class does not right now

* I2S bit initial values can now be specified

* Make the initial values of I2S definable

* Initial I2S value defined for i2s_out for i2s_out_xyzabc_tmc

* CS pin of the motor to be connected to SPI is set to HIGH by default.

* Fix typo in MotorCLass

* Handle UNDEFINED_PIN in digitalWrite and pinMode

Following a suggestion by Bart, this change permits
simplication of spindle code by removing tests that
can be handled more cleanly by just doing nothing
when pinMode or digitalWrite are presented with an
undefined pin.

* CS pin of the motor connected to SPI is set to OUTPUT by default.

Pin mode is not required for I2S out, but it is required for GPIOs.

* Changed the initialization timing of I2S to just after setup().

Whenever digitalWrite() or pinMode() is called, it should not be a problem.

* Remove an unnecessary definition

* The SPI speed of Trinamic was reduced to 100KHz.

Keep it at 100 KHz, as the TMC2130 sometimes fails to initialize when set to approximately 200 KHz or higher.

* Enabled ENABLE_SD_CARD in "config.h".

* Quick fix an error in i2s_out_xyzabc_tmc_class.h.

* Preventing details from being displayed in the top-level file

* Execute the digitalWrite function before pinMode to prevent unintentional LOWs.

* Specifies the initial value of I2S out to prevent CS low for SPI-connected motor drivers.

* Remove unnecessary comments.

* Fixed #430

* Bumped build date to cover #430 fix

* Fixed Tranimic motor class.

- Need to implement the stallguard

* Added AxisMaskSetting and stallguard_debug_mask

* Added $L to one-line help message

* got stallguard features working

- Need to work on reading motor settings after they change.
- void TrinamicDriver :: read_settings() via void motors_read_settings()

* Latest updates

- Got the setting changes updating via SPI
- Need to test some more and write the stall tuning process.
- Need to convert older machine definitions to motor class style.

* Some updates

- Renamed old trinamic files to make sure they do not compile. Will remove soon.
- Cleaned up some code and old machine definitions.

* Updates - Getting Close

-Stallguard tuning and operation looks good
- Fixed limits switch pin numbers for schematic error
- Changed motors_homing_mode to use a mask to allow optimization of SPI
- fix platformio.ini to match Arduino IDE

* Simplify the I2S output pin names (#442)

* Updates to machine defs for motor class

* Fixed issues with trinamic hold/run current

- Both are now float values in Amps. The conversion to what TMCStepper wants is done in the motor class.

* More I2S name simplification (#443)

* Further simplify I2S names

... and remove an obsolete machine definition.

* Fix pin name display

* Oops

* Changes from machine def testing

- Updated grbl date and revisions
- Make test_drive the default machine
- Fixed sign error in servo calibration
- Some config MSG printing cleanup for consistency

* Fix Arduino IDE compilation

Co-authored-by: odaki <odaki@mars.dti.ne.jp>
Co-authored-by: bdring <barton.dring@gmail.com>

* Fix i2s compile problem

Tested with i2s_out_xxyyzz.h on platformio and Arduino

* update TMCStepper library version

* Fix PWM Spindle Issues

- Pulling in enable fixes from master
- `if (_off_with_zero_speed &&  sys.spindle_speed == 0)` somehow got deleted.

* $33 is spindle freq, not type (#457)

* $33 is spindle freq, not type

* Spindle type is read from settings not #define

Co-authored-by: bdring <barton.dring@gmail.com>

* ESP32 Exception decoder (#458)

* Enable ESP32 Exception Decoder in pio monitor

Requires platformio espressif32 platform version >= 1.12.4

* Update platformio.ini

* Faster Trinamic configuration when used with the I2S I/O expander (#445)

* Travis CI build test covers all machine config

* Just copied from Marlin (first commit)

* First ported to Grbl_Esp32 (not yet working)

* Compile i2s only if USE_I2S_EXPANDER is defined.

* rename defines and filenames for I2S I/O expander

* Pulse generator has been implemented

* Fix I2S hander

* remove double lock from i2s_reset_fifo

and make file static functions to static

* Implement stepper stream callbacks

* Fix some bit manipulation bugs

* make function name unique

* make define name unique

* define a base number of extended pins

* machine configuration for the "esp32 printer controller"

* Simplify stepper callbacks

* Cleanup & add some detail comments, stepper bug fix

bugfix
- stepper timer woken up unfortunately
- pulse function should push at least one sample in normal

* Enable the 32-bits stepper streamer

* Faster push samples, comment fixed

* Small comment fix

* Finally, the stepper worked properly

- I2S format corrected
- Fixed an indefinite buffer data being used at idle
- Respond to stop and start requests for steppers

* Code has been reverted to always output a bitstream

* Fix buffer clear method

Fixed a bug in clearing the buffer.
- Fixed a problem in which the lower 8 bits of data were used repeatedly by mistake due to a code error, although originally 32 bits of data should be set.
The order of initialization is changed.
- I first enabled the I2S I/O expander and then the I2S Stepper.
-  The I2S pins are set at the beginning of the init function
-  Enable the I2S ISR after the expander task has been created

Removed code about unused push buffers.

* Implement exclusive access to the internal GPIO variable

and the pulse period parameter is limited to 32 bits (due to the computational speed).

* Code cleanup

- Renamed the function to represent the actual behavior
- I fixed an explanation in the comments that was wrong

* Renamed the function to represent the actual behavior

* Move I/O macros to i2s_ioexpander.h

Removed fastio.h, which affects the entire Grbl source.

* raised the resolution of the stepper pulse

I raised the minimum resolution of the stepper pulse from 4us to 2us.
The buffer size and number of buffers were adjusted.

* Try to implement the I2S reset mechanism(not yet works)

I started implementing the reset process for the I2S I/O expander.
It's still not enough.

* Fix unintended 0 data when I2S restart

* Comments modified to match actual behavior

* pulse clock back to the same speed as the original

* Revert "Merge remote-tracking branch 'upstream/Devt' into i2s_io_expander"

This reverts commit 5f57189f1a, reversing
changes made to c4d827c7a8.

* Revert "Revert "Merge remote-tracking branch 'upstream/Devt' into i2s_io_expander""

This reverts commit ab79f4a59a.

* Fix Guru meditation error

The internal state of the i2s should start with the PAUSED state.

Add exclusive handling in the pulser state check and state change.

* Change function names and internal state names

Based on Mitch-san's suggestion.
https://github.com/bdring/Grbl_Esp32/pull/372#issuecomment-620303937

* Clearly initialize the Pulser state

It's not necessary, but it's easy to understand.
Also, if the enum value changes in the future, the problem is not placed.

* Delete Grbl_Esp32.ino.cpp

* Prevents unintended generation of 0 data (Again)

Fixing the code generated unintended 0 data depending on the timing.
I decided to monitor the arrival status of FIFO data and control conf.tx_stop_en to ensure the generation of zero data regardless of the timing.

* Clean up

Modified to use the correct union as a descriptor queue link.

* Clearing the DMA buffer with a dedicated function

I made sure I didn't write the same code twice.

* Fix some comments

* Cleanup comments

* Test configuration for ESP32 Printer Controller

* Homing support

Move the I2S reset function call from mc_reset() to st_go_idle() to stop motors immediately.

* Update configuration for the test board

Settings for combination testing with servo axes.
- Z servo axis is connected to GPIO15

* Prevents unintended generation of 0 data (change implimentation)

Since the synchronization process by status monitoring does not work as intended, I decided to deal with it by simply waiting for a fixed time.

* Fix about unsigned integer

Fixed a problem with using signed values for unsigned integers.

* Define pin numbers for tne I2S expander

* Update esp32_printer_controller.h

* Fix typo and suppress warning at grbl_trinamic.cpp

* Support the 6axis test board

* Fix the data in the left channel to 0

* Reconfig for JTAG debugging

* Realtime I2S I/O Expander (Quick hack)

Undef "I2S_STEPPER_STREAM" if you intend to use the real-time expander control method.

* Fix I2S reset timing

Fixed an issue where the buffer output was reset at an inappropriate timing.

* Move some definitions to the header to compute the delay

* Prevent overrun when enabled the HardLimit feature

I2S_STEPPER_STREAM only

* In I2S real time mode, FIFO, DMA, ISR and Task are not used

Now, unnecessary code is not executed.

* A paranoid fix for deterring I2S DMA/ISR processing

* Tweaks during PCB testing

- Added some test code
- Fixed B and C step generation.
- Made StepStick and Trinamic versions of machine def file

* Update spi_shift_reg_6axis_xyzabc.h

had wrong pin for !RESET

* Some changes that helped

* Fix comments about I2S channel

* Moved some stuff out of stepper.cpp

I moved some stuff out of stepper.cpp using the existing the global MotorClass interface. They will need to be changed when actual objects are used, but the stepper.cpp file will stay the same.

* More code moving

The goal is to make it easier to merge with MotorClass sometime in the future.

* More code reduction in stepper.cpp plus testing on non SR machine.

- moved I2S_IOEXP_PIN_BASE to config.h
- change axis enable pins to disable pins for consistancy
- Fixed after testing on non SR board with and without RMT.

* Merged in some more motor class stuff

- Uses StandardStepperClass for all motors now to do step, dir and enable.  The Trinamic code is still the old way until the CS is figured out.

* Reviving the lost defines for dual motor

* Fixed a variable name about disable/enable the stepper

* Removing an unused prototype

* Use HAL_digitalWrite instead of I2S_IOEXP_WRITE

* Add HAL_digitalRead()

* Using I2S functions instead of I2S macros

* Remove I2S macros and add definition of bit depth for I2S expander

* Make the definition of I2S_IOEXP_NUM_BITS non-mandatory

If the definition of I2S_IOEXP_NUM_BITS is omitted, 32 is used.

* Check if STEPPER MS has been defined.

* Removed the rounding up of unnecessary pulse width values.

* I2S pin number conversion is done using macros.

* Supports 16-bit shift register (2 chains) configuration.

* Cleaning up ganged axes

- #define USE_GANGED_AXES was not needed. That removed some code.
- ABC axes did not have code for ganged axes...added it.
- Created a test machine def for XXYYCC machine.

* Added squaring of AB&C axes

* Delete Grbl_Esp32.ino.cpp

* Rename Machines files for SR test board

* Fixed a compilation error when not using WiFi.

* Eliminate the error of not finding the required definition

* Fix typo

* Remove an obsoleted function to disable steppers

Use motors_set_disable() instead of set_stepper_disable()

* Update GCodePreprocessor.cpp

* Correspondence to the Setting class

* Quick fix for servo axis

We are in the process of migrating to RcServoClass, so I'm making minimal modifications to make sure it compiles successfully.

* Suppresses compile errors when WIFI is disabled.

* Updated the MotorClass and RcServo Class

* Fix typo in MotorClass

* Fix missing endif in WebSettings

* Fix complile error in WebSetting

* Update espresponse.h

* Fixed a compile error in case-sensitive environments

* Update config.h

* Update debug.ini

Added a configuration skeleton for debugging on ESP-Prog and macOS/Linux as a comment.

* Rename i2s_ioexpander.[cpp|h] to i2s_out.[cpp.|h]

* I2S-related functions and variables changed to be based on "i2s out".

* I2S-related defines changed to be based on "i2s out".

* Change the I2S out task name

* Changed the name of the definition for enabling the I2S OUT feature

* Avoid recursive calls when called  from overridden digitalWrite()

* Fix typo in comment

* Overload digitalWrite() etc (by Mitch Bradley)

Catch up with Mitch-san's Pins branch.
387fc14ea8

* Update i2s_out_xyzabc_tmc to use realtime I2S out by default

* Add IRAM_ATTR attribute to new Arduino-compatible functions.

* Make sure to use the latest version of TMCStepper

* Changed back to using I2S stream mode in i2s_out_xyzabc_tmc.

* Override the CS switch function of the TMCStpper to insert a short delay.

* Clean up

* Fix pinName() overwrite

* "I2S Steps" message when it starts up

I have changed the "I2S Steps" message on startup If the I2S streamer is used.

* Working on Trinamic Driver class

- Trying to make Trinamic driver class work with I2S
- grbl_trinamic.cpp works - class does not right now

* I2S bit initial values can now be specified

* Make the initial values of I2S definable

* Initial I2S value defined for i2s_out for i2s_out_xyzabc_tmc

* CS pin of the motor to be connected to SPI is set to HIGH by default.

* Fix typo in MotorCLass

* Handle UNDEFINED_PIN in digitalWrite and pinMode

Following a suggestion by Bart, this change permits
simplication of spindle code by removing tests that
can be handled more cleanly by just doing nothing
when pinMode or digitalWrite are presented with an
undefined pin.

* CS pin of the motor connected to SPI is set to OUTPUT by default.

Pin mode is not required for I2S out, but it is required for GPIOs.

* Changed the initialization timing of I2S to just after setup().

Whenever digitalWrite() or pinMode() is called, it should not be a problem.

* Remove an unnecessary definition

* The SPI speed of Trinamic was reduced to 100KHz.

Keep it at 100 KHz, as the TMC2130 sometimes fails to initialize when set to approximately 200 KHz or higher.

* Enabled ENABLE_SD_CARD in "config.h".

* Quick fix an error in i2s_out_xyzabc_tmc_class.h.

* Preventing details from being displayed in the top-level file

* Execute the digitalWrite function before pinMode to prevent unintentional LOWs.

* Specifies the initial value of I2S out to prevent CS low for SPI-connected motor drivers.

* Remove unnecessary comments.

* Fixed #430

* Bumped build date to cover #430 fix

* Fixed Tranimic motor class.

- Need to implement the stallguard

* Added AxisMaskSetting and stallguard_debug_mask

* Added $L to one-line help message

* got stallguard features working

- Need to work on reading motor settings after they change.
- void TrinamicDriver :: read_settings() via void motors_read_settings()

* Latest updates

- Got the setting changes updating via SPI
- Need to test some more and write the stall tuning process.
- Need to convert older machine definitions to motor class style.

* Some updates

- Renamed old trinamic files to make sure they do not compile. Will remove soon.
- Cleaned up some code and old machine definitions.

* Updates - Getting Close

-Stallguard tuning and operation looks good
- Fixed limits switch pin numbers for schematic error
- Changed motors_homing_mode to use a mask to allow optimization of SPI
- fix platformio.ini to match Arduino IDE

* Simplify the I2S output pin names (#442)

* Updates to machine defs for motor class

* Fixed issues with trinamic hold/run current

- Both are now float values in Amps. The conversion to what TMCStepper wants is done in the motor class.

* More I2S name simplification (#443)

* Further simplify I2S names

... and remove an obsolete machine definition.

* Fix pin name display

* Oops

* Changes from machine def testing

- Updated grbl date and revisions
- Make test_drive the default machine
- Fixed sign error in servo calibration
- Some config MSG printing cleanup for consistency

* Implement I2s_out_shiftout()

- Shifts out the state value of the current pin with a bit bang.
- Stops DMA/ISR when i2s_out_set_passthrough() is called.
- Resume DMA by initializing the buffer when i2s_out_set_stepping() is called.

* Reduce delay in I2S real-time mode

In I2S real-time mode, the delay time for applying changes can be reduced.

* Fix Arduino IDE compilation

* Arduino IDE support

* Functionalize writing to I2S_CONF_SINGLE_DATA_REG

* Switching I2S to static I2S mode when the stepper goes into pass-through mode

* Delay adjustment

* Cleanup

* Update lib_deps in platformio.ini

Clearly defined that TMCStepper v0.7.0 or later is required.

* Fix reset duaring the WAITING state

* Fix i2sOutTask

Prevents the execution of unintended pulser callbacks

* QuickFix unintended pulse function callbacks

* Fixed a problem with I2S state transitions (in progress)

* Fix step to end in the homing locating phase

* Implement i2s_fillout_dma_buffer()

The process of filling the buffer is now a function.

* Update build date

Co-authored-by: bdring <barton.dring@gmail.com>
Co-authored-by: Mitch Bradley <wmb@firmworks.com>

* Fixed missing #define GRBL_VERSION "1.3a"

- got lost in a web editor conflict fix attempt

* Hierarchical setting names (#444)

* Hierarchical setting names

* Bump date

* Fix crash due to large axis values

Buffer for axis value was failing on "-10000.111" sized values.

* Update grbl.h

* Sensor less homing with I2S OUT (#468)

* Add a delay to the beginning of the homing.

Wait for all empty values in the buffer to be output.

* Fixed StallGuard range.

The thresholds for stallGuard2 range from -64 to 63.

* Fix default hold current

The Hold current setting appears to be a current value, not a percentage.

* Update grbl.h

* Remove the delay

It does not needed for Homing.

* 6 Pack controller definition files.

Early stage testing

* More 6-pack updates

* 6_pack updates

* Authentication for new settings (#470)

* Authentication

* Remove password first, so $ form works

* Fix empty error message in localfs/run

* Fixed WiFi/ListAPs problem in AP mode

AP listings in AP mode were unreliable.  The first try
would often fail, the second try would succeed, and subsequent
tries would always fail.  Now it works reliably.

* Fixed $text=val when val contains =

* Fixed web commands with key=value params

* Added local file listing commands

* Cleaned up include structure

.. to eliminate circular dependencies.
Most .cpp files now only need include grbl.h
to get all the common stuff.  Includes other
than grbl.h are for external libraries that are
specific to one module.

* Authentication working with new settings.

This entailed re-factoring of the command and GCode
line parsing and execution structure so lots of
formerly-separate code paths no go through common
code.  In particular, the GCode line normalization -
space/comment removal and upper case conversion -
is now done in one place, just prior to GCode parsing.
That eliminates the need to do it separately for
various input paths like serial, SPIFFS, SD, web, etc.
The normalization code is quite efficient so the impact
on performance should be minimal.
Another related change was to maintain separate line
input buffers for different "clients", so, for example,
telnet input and serial input are separate - not
interspersed.
Every setting and command can have permissions, so
authentication could potentially be applied to legacy
GRBL settings in addition to WebUI settings.  For now,
all legacy settings and commands are open, requiring
no authentication.
Authentication can be supplied via login in the WebUI
case, or on the command line for various serial-like
input sources.  Authentication applies also to listing
functions, so you cannot bypass authentication with $S.

* Fixed path problem with localfs/run

* Trim whitespace on non-string settings values

* Hierarchical names for grbl commands

* Compile-tested with different config options

Cleaned up the include structure
Changed level_authenticate_type to auth_t for brevity

* Update 6_pack_stepstick_v1.h

* Fixed jog botch

* Update 6_pack_stepstick_v1.h

* Bit mask (#477)

* Infer limit_mask from limit pin definitions

Also get rid of some pointless definitions.

* Use bit(n) for (1<<n)

* Added limit_mask_initialization to limits_init()

Co-authored-by: bdring <barton.dring@gmail.com>

* SD end bug and paren comment bug

* Some cleanup of the trinamic motor class

- Cleanup of the way the Stealth, CoolStep, Stallguard modes are set.
- More work needs to be done to optimize the mode values.

* Fix double-enabled probrem (#483)

If you explicitly specify enable for a feature that has already been defined, it is doubly enabled.

* Acceleration value has unified to mm/sec^2 (#484)

* Fixed python build problem (#480)

* A little Trinamic Driver class cleanup

* Adding 10V Spindle

* Fixed lowrider and platformio.ini

Co-authored-by: Mitch Bradley <wmb@firmworks.com>
Co-authored-by: odaki <odaki@mars.dti.ne.jp>
This commit is contained in:
bdring
2020-07-20 16:40:41 -05:00
committed by GitHub
parent 947ae88859
commit d35d67e195
118 changed files with 8591 additions and 4542 deletions

View File

@@ -20,14 +20,11 @@
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#include "config.h" #include "grbl.h"
#ifdef ENABLE_BLUETOOTH #ifdef ENABLE_BLUETOOTH
#include <Preferences.h>
#include "BluetoothSerial.h" #include "BluetoothSerial.h"
#include "BTconfig.h" #include "BTconfig.h"
#include "commands.h"
#include "report.h"
BTConfig bt_config; BTConfig bt_config;
BluetoothSerial SerialBT; BluetoothSerial SerialBT;
@@ -93,8 +90,7 @@ const char* BTConfig::info() {
bool BTConfig::isBTnameValid(const char* hostname) { bool BTConfig::isBTnameValid(const char* hostname) {
//limited size //limited size
char c; char c;
if (strlen(hostname) > MAX_BTNAME_LENGTH || strlen(hostname) < MIN_BTNAME_LENGTH) // length is checked automatically by string setting
return false;
//only letter and digit //only letter and digit
for (int i = 0; i < strlen(hostname); i++) { for (int i = 0; i < strlen(hostname); i++) {
c = hostname[i]; c = hostname[i];
@@ -116,16 +112,10 @@ const char* BTConfig::device_address() {
* begin WiFi setup * begin WiFi setup
*/ */
void BTConfig::begin() { void BTConfig::begin() {
Preferences prefs;
//stop active services //stop active services
end(); end();
prefs.begin(NAMESPACE, true); _btname = bt_name->get();
//Get hostname if (wifi_radio_mode->get() == ESP_BT) {
String defV = DEFAULT_BT_NAME;
_btname = prefs.getString(BT_NAME_ENTRY, defV);
int8_t wifiMode = prefs.getChar(ESP_RADIO_MODE, DEFAULT_RADIO_MODE);
prefs.end();
if (wifiMode == ESP_BT) {
if (!SerialBT.begin(_btname)) if (!SerialBT.begin(_btname))
report_status_message(STATUS_BT_FAIL_BEGIN, CLIENT_ALL); report_status_message(STATUS_BT_FAIL_BEGIN, CLIENT_ALL);
else { else {
@@ -146,22 +136,9 @@ void BTConfig::end() {
* Reset ESP * Reset ESP
*/ */
void BTConfig::reset_settings() { void BTConfig::reset_settings() {
Preferences prefs; bt_name->setDefault();
prefs.begin(NAMESPACE, false); wifi_radio_mode->setDefault();
String sval; grbl_send(CLIENT_ALL, "[MSG:BT reset done]\r\n");
int8_t bbuf;
bool error = false;
sval = DEFAULT_BT_NAME;
if (prefs.putString(BT_NAME_ENTRY, sval) == 0)
error = true;
bbuf = DEFAULT_RADIO_MODE;
if (prefs.putChar(ESP_RADIO_MODE, bbuf) == 0)
error = true;
prefs.end();
if (error)
grbl_send(CLIENT_ALL, "[MSG:BT reset error]\r\n");
else
grbl_send(CLIENT_ALL, "[MSG:BT reset done]\r\n");
} }
/** /**

View File

@@ -21,9 +21,6 @@
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif #endif
//Preferences entries
#define BT_NAME_ENTRY "BT_NAME"
//defaults values //defaults values
#define DEFAULT_BT_NAME "btgrblesp" #define DEFAULT_BT_NAME "btgrblesp"

View File

@@ -134,9 +134,9 @@ void atari_home_task(void* pvParameters) {
if (digitalRead(REED_SW_PIN) == 0) { if (digitalRead(REED_SW_PIN) == 0) {
// see if reed switch is grounded // see if reed switch is grounded
inputBuffer.push("G4P0.1\n"); // dramtic pause inputBuffer.push("G4P0.1\n"); // dramtic pause
sys_position[X_AXIS] = ATARI_HOME_POS * settings.steps_per_mm[X_AXIS]; sys_position[X_AXIS] = ATARI_HOME_POS * axis_settings[X_AXIS]->steps_per_mm->get();
sys_position[Y_AXIS] = 0.0; sys_position[Y_AXIS] = 0.0;
sys_position[Z_AXIS] = 1.0 * settings.steps_per_mm[Y_AXIS]; sys_position[Z_AXIS] = 1.0 * axis_settings[Y_AXIS]->steps_per_mm->get();
gc_sync_position(); gc_sync_position();
plan_sync_position(); plan_sync_position();
sprintf(gcode_line, "G90G0X%3.2f\r", ATARI_PAPER_WIDTH); // alway return to right side to reduce home travel stalls sprintf(gcode_line, "G90G0X%3.2f\r", ATARI_PAPER_WIDTH); // alway return to right side to reduce home travel stalls

View File

@@ -0,0 +1,197 @@
/*
custom_code_template.cpp (copy and use your machine name)
Part of Grbl_ESP32
copyright (c) 2020 - Bart Dring. This file was intended for use on the ESP32
...add your date and name here.
CPU. Do not use this with Grbl for atMega328P
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl_ESP32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
=======================================================================
This is a template for user-defined C++ code functions. Grbl can be
configured to call some optional functions, enabled by #define statements
in the machine definition .h file. Implement the functions thus enabled
herein. The possible functions are listed and described below.
To use this file, copy it to a name of your own choosing, and also copy
Machines/template.h to a similar name.
Example:
Machines/my_machine.h
Custom/my_machine.cpp
Edit machine.h to include your Machines/my_machine.h file
Edit Machines/my_machine.h according to the instructions therein.
Fill in the function definitions below for the functions that you have
enabled with USE_ defines in Machines/my_machine.h
===============================================================================
*/
#ifdef USE_MACHINE_INIT
/*
machine_init() is called when Grbl_ESP32 first starts. You can use it to do any
special things your machine needs at startup.
*/
#define STEPPERS_DISABLE_PIN_X 138
#define STEPPERS_DISABLE_PIN_Y 134
#define STEPPERS_DISABLE_PIN_Z 131
#define STEPPERS_DISABLE_PIN_A 139
#define FAN1_PIN 13
#define FAN2_PIN 142
#define FAN3_PIN 143
#define BED_PIN 4
#define NOZZLE_PIN 2
void machine_init()
{
// Enable steppers
digitalWrite(STEPPERS_DISABLE_PIN_X, LOW); // enable
digitalWrite(STEPPERS_DISABLE_PIN_Y, LOW); // enable
digitalWrite(STEPPERS_DISABLE_PIN_Z, LOW); // enable
digitalWrite(STEPPERS_DISABLE_PIN_A, LOW); // enable
// digitalWrite(FAN1_PIN, LOW); // comment out for JTAG debugging
digitalWrite(FAN2_PIN, LOW); // disable
digitalWrite(FAN3_PIN, LOW); // disable
digitalWrite(BED_PIN, LOW); // disable
digitalWrite(NOZZLE_PIN, LOW); // disable
}
#endif
#ifdef USE_CUSTOM_HOMING
/*
user_defined_homing() is called at the begining of the normal Grbl_ESP32 homing
sequence. If user_defined_homing() returns false, the rest of normal Grbl_ESP32
homing is skipped if it returns false, other normal homing continues. For
example, if you need to manually prep the machine for homing, you could implement
user_defined_homing() to wait for some button to be pressed, then return true.
*/
bool user_defined_homing()
{
// True = done with homing, false = continue with normal Grbl_ESP32 homing
return true;
}
#endif
#ifdef USE_KINEMATICS
/*
Inverse Kinematics converts X,Y,Z cartesian coordinate to the steps
on your "joint" motors. It requires the following three functions:
*/
/*
inverse_kinematics() looks at incoming move commands and modifies
them before Grbl_ESP32 puts them in the motion planner.
Grbl_ESP32 processes arcs by converting them into tiny little line segments.
Kinematics in Grbl_ESP32 works the same way. Search for this function across
Grbl_ESP32 for examples. You are basically converting cartesian X,Y,Z... targets to
target = an N_AXIS array of target positions (where the move is supposed to go)
pl_data = planner data (see the definition of this type to see what it is)
position = an N_AXIS array of where the machine is starting from for this move
*/
void inverse_kinematics(float *target, plan_line_data_t *pl_data, float *position)
{
// this simply moves to the target. Replace with your kinematics.
mc_line(target, pl_data);
}
/*
kinematics_pre_homing() is called before normal homing
You can use it to do special homing or just to set stuff up
cycle_mask is a bit mask of the axes being homed this time.
*/
bool kinematics_pre_homing(uint8_t cycle_mask))
{
return false; // finish normal homing cycle
}
/*
kinematics_post_homing() is called at the end of normal homing
*/
void kinematics_post_homing()
{
}
#endif
#ifdef USE_FWD_KINEMATIC
/*
The status command uses forward_kinematics() to convert
your motor positions to cartesian X,Y,Z... coordinates.
Convert the N_AXIS array of motor positions to cartesian in your code.
*/
void forward_kinematics(float *position)
{
// position[X_AXIS] =
// position[Y_AXIS] =
}
#endif
#ifdef USE_TOOL_CHANGE
/*
user_tool_change() is called when tool change gcode is received,
to perform appropriate actions for your machine.
*/
void user_tool_change(uint8_t new_tool)
{
}
#endif
#if defined(MACRO_BUTTON_0_PIN) || defined(MACRO_BUTTON_1_PIN) || defined(MACRO_BUTTON_2_PIN)
/*
options. user_defined_macro() is called with the button number to
perform whatever actions you choose.
*/
void user_defined_macro(uint8_t index)
{
}
#endif
#ifdef USE_M30
/*
user_m30() is called when an M30 gcode signals the end of a gcode file.
*/
void user_m30()
{
}
#endif
#ifdef USE_MACHINE_TRINAMIC_INIT
/*
machine_triaminic_setup() replaces the normal setup of trinamic
drivers with your own code. For example, you could setup StallGuard
*/
void machine_trinamic_setup()
{
}
#endif
// If you add any additional functions specific to your machine that
// require calls from common code, guard their calls in the common code with
// #ifdef USE_WHATEVER and add function prototypes (also guarded) to grbl.h

View File

@@ -65,10 +65,6 @@ static float last_radius = 0;
// return false to complete normal home // return false to complete normal home
// return true to exit normal homing // return true to exit normal homing
bool kinematics_pre_homing(uint8_t cycle_mask) { bool kinematics_pre_homing(uint8_t cycle_mask) {
// cycle mask not used for polar coaster
// zero the axes that are not homed
sys_position[Y_AXIS] = 0.0f;
sys_position[Z_AXIS] = SERVO_Z_RANGE_MAX * settings.steps_per_mm[Z_AXIS]; // Move pen up.
return false; // finish normal homing cycle return false; // finish normal homing cycle
} }

View File

@@ -22,6 +22,7 @@
#include "WiFi.h" #include "WiFi.h"
#include "Spindles/SpindleClass.cpp" #include "Spindles/SpindleClass.cpp"
#include "Motors/MotorClass.cpp"
// Declare system global variable structure // Declare system global variable structure
system_t sys; system_t sys;
@@ -41,6 +42,9 @@ Spindle *spindle;
void setup() { void setup() {
#ifdef USE_I2S_OUT
i2s_out_init(); // The I2S out must be initialized before it can access the expanded GPIO port
#endif
WiFi.persistent(false); WiFi.persistent(false);
WiFi.disconnect(true); WiFi.disconnect(true);
WiFi.enableSTA(false); WiFi.enableSTA(false);
@@ -60,6 +64,7 @@ void setup() {
#endif #endif
settings_init(); // Load Grbl settings from EEPROM settings_init(); // Load Grbl settings from EEPROM
stepper_init(); // Configure stepper pins and interrupt timers stepper_init(); // Configure stepper pins and interrupt timers
init_motors();
system_ini(); // Configure pinout pins and pin-change interrupt (Renamed due to conflict with esp32 files) system_ini(); // Configure pinout pins and pin-change interrupt (Renamed due to conflict with esp32 files)
memset(sys_position, 0, sizeof(sys_position)); // Clear machine position. memset(sys_position, 0, sizeof(sys_position)); // Clear machine position.
#ifdef USE_PEN_SERVO #ifdef USE_PEN_SERVO
@@ -89,9 +94,9 @@ void setup() {
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into // not after disabling the alarm locks. Prevents motion startup blocks from crashing into
// things uncontrollably. Very bad. // things uncontrollably. Very bad.
#ifdef HOMING_INIT_LOCK #ifdef HOMING_INIT_LOCK
if (bit_istrue(settings.flags, BITFLAG_HOMING_ENABLE)) sys.state = STATE_ALARM; if (homing_enable->get()) sys.state = STATE_ALARM;
#endif #endif
spindle_select(SPINDLE_TYPE); spindle_select();
#ifdef ENABLE_WIFI #ifdef ENABLE_WIFI
wifi_config.begin(); wifi_config.begin();
#endif #endif

171
Grbl_Esp32/JSONencoder.cpp Normal file
View File

@@ -0,0 +1,171 @@
// Class for creating JSON-encoded strings.
#include "grbl.h"
#include "JSONencoder.h"
// Constructor. If _pretty is true, newlines are
// inserted into the JSON string for easy reading.
JSONencoder::JSONencoder(bool pretty) :
pretty(pretty),
level(0),
str("")
{
count[level] = 0;
}
// Private function to add commas between
// elements as needed, omitting the comma
// before the first element in a list.
// If pretty-printing is enabled, a newline
// is added after the comma.
void JSONencoder::comma_line() {
if (count[level]) {
add(',');
line();
}
count[level]++;
}
// Private function to add commas between
// elements as needed, omitting the comma
// before the first element in a list.
void JSONencoder::comma() {
if (count[level]) {
add(',');
}
count[level]++;
}
// Private function to add a name enclosed with quotes.
void JSONencoder::quoted(const char *s)
{
add('"');
str.concat(s);
add('"');
}
// Private function to increment the nesting level.
// It's necessary to account for the level in order
// to handle commas properly, as each level must
// know when to omit the comma.
void JSONencoder::inc_level() {
if (++level == MAX_JSON_LEVEL) {
--level;
}
count[level] = 0;
}
// Private function to increment the nesting level.
void JSONencoder::dec_level() {
--level;
}
// Private function to implement pretty-printing
void JSONencoder::line() {
if (pretty) {
add('\n');
for (int i=0; i < 2*level; i++) {
add(' ');
}
}
}
// Constructor that supplies a default falue for "pretty"
JSONencoder::JSONencoder() :
JSONencoder(false)
{ }
// Begins the JSON encoding process, creating an unnamed object
void JSONencoder::begin() {
begin_object();
}
// Finishes the JSON encoding process, closing the unnamed object
// and returning the encoded string
String JSONencoder::end() {
end_object();
return str;
}
// Starts a member element.
void JSONencoder::begin_member(const char *tag) {
comma_line();
quoted(tag);
add(':');
}
// Starts an array with "tag":[
void JSONencoder::begin_array(const char *tag) {
begin_member(tag);
add('[');
inc_level();
line();
}
// Ends an array with ]
void JSONencoder::end_array() {
dec_level();
line();
add(']');
}
// Starts an object with {.
// If you need a named object you must call begin_member() first.
void JSONencoder::begin_object() {
comma_line();
add('{');
inc_level();
}
// Ends an object with }.
void JSONencoder::end_object() {
dec_level();
if (count[level+1] > 1) {
line();
}
add('}');
}
// Creates a "tag":"value" member from a C-style string
void JSONencoder::member(const char *tag, const char *value) {
begin_member(tag);
quoted(value);
}
// Creates a "tag":"value" member from an Arduino string
void JSONencoder::member(const char *tag, String value) {
begin_member(tag);
quoted(value.c_str());
}
// Creates a "tag":"value" member from an integer
void JSONencoder::member(const char *tag, int value) {
member(tag, String(value));
}
// Creates an Esp32_WebUI configuration item specification from
// a value passed in as a C-style string.
void JSONencoder::begin_webui(const char *p, const char *help, const char *type, const char *val) {
begin_object();
member("F", "network");
member("P", p);
member("H", help);
member("T", type);
member("V", val);
}
// Creates an Esp32_WebUI configuration item specification from
// an integer value.
void JSONencoder::begin_webui(const char *p, const char *help, const char *type, int val) {
begin_webui(p, help, type, String(val).c_str());
}
// Creates an Esp32_WebUI configuration item specification from
// a C-style string value, with additional min and max arguments.
void JSONencoder::begin_webui(const char *p, const char *help, const char *type, const char *val, int min, int max) {
begin_webui(p, help, type, val);
member("S", max);
member("M", min);
}

76
Grbl_Esp32/JSONencoder.h Normal file
View File

@@ -0,0 +1,76 @@
// Class for creating JSON-encoded strings.
#pragma once
#define MAX_JSON_LEVEL 16
class JSONencoder {
private:
bool pretty;
int level;
String str;
int count[MAX_JSON_LEVEL];
void add(char c) { str += c; }
void comma_line();
void comma();
void quoted(const char *s);
void inc_level();
void dec_level();
void line();
public:
// Constructor; set _pretty true for pretty printing
JSONencoder(bool pretty);
// If you don't set _pretty it defaults to false
JSONencoder();
// begin() starts the encoding process.
void begin();
// end() returns the encoded string
String end();
// member() creates a "tag":"value" element
void member(const char *tag, const char *value);
void member(const char *tag, String value);
void member(const char *tag, int value);
// begin_array() starts a "tag":[ array element
void begin_array(const char *tag);
// end_array() closes the array with ]
void end_array();
// begin_object() starts an object with {
void begin_object();
// end_object() closes the object with }
void end_object();
// begin_member() starts the creation of a member.
// The only case where you need to use it directly
// is when you want a member whose value is an object.
void begin_member(const char *tag);
// The begin_webui() methods are specific to Esp3D_WebUI
// WebUI sends JSON objects to the UI to generate configuration
// page entries. Each object describes a named setting with a
// type, current value, and a description of the possible values.
// The possible values can either be a minumum and maximum
// integer value, min/max string length, or an enumeration list.
// When the user chooses a value, this command is sent back:
// [ESP401]P=p T=type V=value
// P: parameter name
// T: type
// M: min_val
// S: max_val
// O: options:[ { "name", "value" } ... ]
// V: currentvalue
// H: label
// F: F ("network", used for filtering)
// If M and S are not supplied, they are inferred from type:
// B => -127 .. 255
// S => 0 .. 255
// A => 7 .. 15 (0.0.0.0 .. 255.255.255.255)
// I => 0 .. 2^31-1
void begin_webui(const char *p, const char *help, const char *type, const char *val);
void begin_webui(const char *p, const char *help, const char *type, const int val);
void begin_webui(const char *p, const char *help, const char *type, const char *val, int min, int max);
};

View File

@@ -32,7 +32,6 @@
#define Z_STEP_PIN GPIO_NUM_27 #define Z_STEP_PIN GPIO_NUM_27
#define Z_DIRECTION_PIN GPIO_NUM_33 #define Z_DIRECTION_PIN GPIO_NUM_33
#define LIMIT_MASK B111
#define X_LIMIT_PIN GPIO_NUM_2 // labeled X Limit #define X_LIMIT_PIN GPIO_NUM_2 // labeled X Limit
#define Y_LIMIT_PIN GPIO_NUM_4 // labeled Y Limit #define Y_LIMIT_PIN GPIO_NUM_4 // labeled Y Limit
#define Z_LIMIT_PIN GPIO_NUM_15 // labeled Z Limit #define Z_LIMIT_PIN GPIO_NUM_15 // labeled Z Limit

View File

@@ -32,7 +32,6 @@
#define Z_STEP_PIN GPIO_NUM_27 #define Z_STEP_PIN GPIO_NUM_27
#define Z_DIRECTION_PIN GPIO_NUM_33 #define Z_DIRECTION_PIN GPIO_NUM_33
#define LIMIT_MASK B111
#define X_LIMIT_PIN GPIO_NUM_17 #define X_LIMIT_PIN GPIO_NUM_17
#define Y_LIMIT_PIN GPIO_NUM_4 #define Y_LIMIT_PIN GPIO_NUM_4
#define Z_LIMIT_PIN GPIO_NUM_16 #define Z_LIMIT_PIN GPIO_NUM_16
@@ -40,12 +39,12 @@
#ifdef HOMING_CYCLE_0 #ifdef HOMING_CYCLE_0
#undef HOMING_CYCLE_0 #undef HOMING_CYCLE_0
#endif #endif
#define HOMING_CYCLE_0 (1<<Z_AXIS) // Z first #define HOMING_CYCLE_0 bit(Z_AXIS) // Z first
#ifdef HOMING_CYCLE_1 #ifdef HOMING_CYCLE_1
#undef HOMING_CYCLE_1 #undef HOMING_CYCLE_1
#endif #endif
#define HOMING_CYCLE_1 ((1<<X_AXIS)|(1<<Y_AXIS)) #define HOMING_CYCLE_1 (bit(X_AXIS)|bit(Y_AXIS))
#ifdef HOMING_CYCLE_2 #ifdef HOMING_CYCLE_2
#undef HOMING_CYCLE_2 #undef HOMING_CYCLE_2

View File

@@ -36,7 +36,6 @@
#define SPINDLE_OUTPUT_PIN GPIO_NUM_2 #define SPINDLE_OUTPUT_PIN GPIO_NUM_2
#define SPINDLE_ENABLE_PIN GPIO_NUM_22 #define SPINDLE_ENABLE_PIN GPIO_NUM_22
#define LIMIT_MASK B11
#define X_LIMIT_PIN GPIO_NUM_17 #define X_LIMIT_PIN GPIO_NUM_17
#define Y_LIMIT_PIN GPIO_NUM_4 #define Y_LIMIT_PIN GPIO_NUM_4
// #define Z_LIMIT_PIN GPIO_NUM_16 // #define Z_LIMIT_PIN GPIO_NUM_16

View File

@@ -1,5 +1,5 @@
/* /*
external_driver_4x.h 4axis_external_driver_4x.h
Part of Grbl_ESP32 Part of Grbl_ESP32
Pin assignments for the buildlog.net 4-axis external driver board Pin assignments for the buildlog.net 4-axis external driver board
@@ -56,11 +56,8 @@
#define Y_LIMIT_PIN GPIO_NUM_35 #define Y_LIMIT_PIN GPIO_NUM_35
#define Z_LIMIT_PIN GPIO_NUM_36 #define Z_LIMIT_PIN GPIO_NUM_36
#if (N_AXIS == 3) #if (N_AXIS != 3)
#define LIMIT_MASK B0111
#else
#define A_LIMIT_PIN GPIO_NUM_39 #define A_LIMIT_PIN GPIO_NUM_39
#define LIMIT_MASK B1111
#endif #endif
#define PROBE_PIN GPIO_NUM_32 #define PROBE_PIN GPIO_NUM_32

View File

@@ -0,0 +1,129 @@
/*
6_pack_stepstick_v1.h
Covers all V1 versions V1p0, V1p1, etc
Part of Grbl_ESP32
Pin assignments for the ESP32 I2S 6-axis board
2018 - Bart Dring
2020 - Mitch Bradley
2020 - Michiyasu Odaki
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
#define MACHINE_NAME "6 Pack Controller V1 (StepStick)"
#ifdef N_AXIS
#undef N_AXIS
#endif
#define N_AXIS 6
#ifdef ENABLE_SD_CARD
#undef ENABLE_SD_CARD
#endif
// === Special Features
// I2S (steppers & other output-only pins)
#define USE_I2S_OUT
// Define USE_I2S_OUT_STREAM if buffering is used.
// (there will be a delay between the specified I/O operation and the actual I/O execution)
#define USE_I2S_OUT_STREAM
#undef USE_RMT_STEPS
#define USE_STEPSTICK // makes sure MS1,2,3 !reset and !sleep are set
#define I2S_OUT_BCK GPIO_NUM_22
#define I2S_OUT_WS GPIO_NUM_17
#define I2S_OUT_DATA GPIO_NUM_21
#define STEPPER_X_MS3 I2SO(3) // X_CS
#define STEPPER_Y_MS3 I2SO(6) // Y_CS
#define STEPPER_Z_MS3 I2SO(11) // Z_CS
#define STEPPER_A_MS3 I2SO(14) // A_CS
#define STEPPER_B_MS3 I2SO(19) // B_CS
#define STEPPER_C_MS3 I2SO(22) // C_CS
#define STEPPER_RESET GPIO_NUM_19
#define X_DISABLE_PIN I2SO(0)
#define X_DIRECTION_PIN I2SO(1)
#define X_STEP_PIN I2SO(2)
#define Y_DIRECTION_PIN I2SO(4)
#define Y_STEP_PIN I2SO(5)
#define Y_DISABLE_PIN I2SO(7)
#define Z_DISABLE_PIN I2SO(8)
#define Z_DIRECTION_PIN I2SO(9)
#define Z_STEP_PIN I2SO(10)
#define A_DIRECTION_PIN I2SO(12)
#define A_STEP_PIN I2SO(13)
#define A_DISABLE_PIN I2SO(15)
#define B_DISABLE_PIN I2SO(16)
#define B_DIRECTION_PIN I2SO(17)
#define B_STEP_PIN I2SO(18)
#define C_DIRECTION_PIN I2SO(20)
#define C_STEP_PIN I2SO(21)
#define C_DISABLE_PIN I2SO(23)
#define X_LIMIT_PIN GPIO_NUM_33
#define Y_LIMIT_PIN GPIO_NUM_32
#define Z_LIMIT_PIN GPIO_NUM_35
#define A_LIMIT_PIN GPIO_NUM_34
#define B_LIMIT_PIN GPIO_NUM_39
#define C_LIMIT_PIN GPIO_NUM_36
#define PROBE_PIN GPIO_NUM_25
// 0-10v CNC Module in Socket #3
// Control...Set PD001 to 1 if enable is connected 0 is panel should be used
// Freq...Set PD002 to 0
// Freq input...Set PD070 to 0 for 0-10V
#define SPINDLE_TYPE SPINDLE_TYPE_10V
#define SPINDLE_OUTPUT_PIN GPIO_NUM_26
#define SPINDLE_FORWARD_PIN GPIO_NUM_4
#define SPINDLE_REVERSE_PIN GPIO_NUM_16
// Example 5V output CNC module in socket #3
/*
#define SPINDLE_TYPE SPINDLE_TYPE_PWM
#define SPINDLE_OUTPUT_PIN GPIO_NUM_26
#define SPINDLE_ENABLE_PIN GPIO_NUM_4
#define SPINDLE_DIR_PIN GPIO_NUM_16
#define COOLANT_MIST_PIN GPIO_NUM_27
*/
/*
// Example (4x) 5V Buffer Output on socket #5
#define SPINDLE_TYPE SPINDLE_TYPE_RELAY
#define SPINDLE_OUTPUT_PIN I2SO(24)
#define SPINDLE_DIR_PIN I2SO(25)
#define COOLANT_MIST_PIN I2SO(26)
#define COOLANT_FLOOD_PIN I2SO(27)
*/
/*
// RS485 In socket #3
#define SPINDLE_TYPE SPINDLE_TYPE_HUANYANG // only one spindle at a time
#define HUANYANG_TXD_PIN GPIO_NUM_26
#define HUANYANG_RTS_PIN GPIO_NUM_4
#define HUANYANG_RXD_PIN GPIO_NUM_16
*/
// === Default settings
#define DEFAULT_STEP_PULSE_MICROSECONDS I2S_OUT_USEC_PER_PULSE

View File

@@ -0,0 +1,105 @@
/*
6_pack_trinamic_V1.h
Part of Grbl_ESP32
Pin assignments for the ESP32 SPI 6-axis board
2018 - Bart Dring
2020 - Mitch Bradley
2020 - Michiyasu Odaki
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
#define MACHINE_NAME "6 Pack Controller V1 (Trinamic)"
#ifdef N_AXIS
#undef N_AXIS
#endif
#define N_AXIS 6
// === Special Features
// I2S (steppers & other output-only pins)
#define USE_I2S_OUT
// Define USE_I2S_OUT_STREAM if buffering is used.
// (there will be a delay between the specified I/O operation and the actual I/O execution)
#define USE_I2S_OUT_STREAM
#undef USE_RMT_STEPS
#define I2S_OUT_BCK GPIO_NUM_22
#define I2S_OUT_WS GPIO_NUM_17
#define I2S_OUT_DATA GPIO_NUM_21
#define TRINAMIC_RUN_MODE TRINAMIC_MODE_COOLSTEP
#define TRINAMIC_HOMING_MODE TRINAMIC_MODE_COOLSTEP
#define X_TRINAMIC_DRIVER 2130
#define X_DISABLE_PIN I2SO(0)
#define X_DIRECTION_PIN I2SO(1)
#define X_STEP_PIN I2SO(2)
#define X_CS_PIN I2SO(3)
#define X_RSENSE TMC2130_RSENSE_DEFAULT
#define Y_TRINAMIC_DRIVER 2130
#define Y_DIRECTION_PIN I2SO(4)
#define Y_STEP_PIN I2SO(5)
#define Y_DISABLE_PIN I2SO(7)
#define Y_CS_PIN I2SO(6)
#define Y_RSENSE X_RSENSE
#define Z_TRINAMIC_DRIVER 2130
#define Z_DISABLE_PIN I2SO(8)
#define Z_DIRECTION_PIN I2SO(9)
#define Z_STEP_PIN I2SO(10)
#define Z_CS_PIN I2SO(11)
#define Z_RSENSE X_RSENSE
#define A_TRINAMIC_DRIVER 2130
#define A_DIRECTION_PIN I2SO(12)
#define A_STEP_PIN I2SO(13)
#define A_DISABLE_PIN I2SO(15)
#define A_CS_PIN I2SO(14)
#define A_RSENSE X_RSENSE
#define B_TRINAMIC_DRIVER 2130
#define B_DISABLE_PIN I2SO(16)
#define B_DIRECTION_PIN I2SO(17)
#define B_STEP_PIN I2SO(18)
#define B_CS_PIN I2SO(19)
#define B_RSENSE X_RSENSE
#define C_TRINAMIC_DRIVER 2130
#define C_DIRECTION_PIN I2SO(20)
#define C_STEP_PIN I2SO(21)
#define C_DISABLE_PIN I2SO(23)
#define C_CS_PIN I2SO(22)
#define C_RSENSE X_RSENSE
// 0-10v CNC Module in Socket #3
#define SPINDLE_TYPE SPINDLE_TYPE_PWM
#define SPINDLE_OUTPUT_PIN GPIO_NUM_26
#define SPINDLE_ENABLE_PIN GPIO_NUM_4
#define SPINDLE_DIR_PIN GPIO_NUM_16
#define X_LIMIT_PIN GPIO_NUM_33
#define Y_LIMIT_PIN GPIO_NUM_32
#define Z_LIMIT_PIN GPIO_NUM_35
#define A_LIMIT_PIN GPIO_NUM_34
#define B_LIMIT_PIN GPIO_NUM_39
#define C_LIMIT_PIN GPIO_NUM_36
#define PROBE_PIN GPIO_NUM_25
#define COOLANT_MIST_PIN GPIO_NUM_2
// === Default settings
#define DEFAULT_STEP_PULSE_MICROSECONDS I2S_OUT_USEC_PER_PULSE

View File

@@ -28,7 +28,7 @@
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>. along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define MACHINE_NAME "ATARI_1020" #define MACHINE_NAME "MACHINE_ATARI_1020"
#define CUSTOM_CODE_FILENAME "Custom/atari_1020.cpp" #define CUSTOM_CODE_FILENAME "Custom/atari_1020.cpp"
@@ -36,8 +36,6 @@
#undef USE_RMT_STEPS #undef USE_RMT_STEPS
#endif #endif
#define USE_UNIPOLAR
#define X_UNIPOLAR #define X_UNIPOLAR
#define X_PIN_PHASE_0 GPIO_NUM_13 #define X_PIN_PHASE_0 GPIO_NUM_13
#define X_PIN_PHASE_1 GPIO_NUM_21 #define X_PIN_PHASE_1 GPIO_NUM_21
@@ -56,7 +54,7 @@
#ifdef HOMING_CYCLE_0 #ifdef HOMING_CYCLE_0
#undef HOMING_CYCLE_0 #undef HOMING_CYCLE_0
#endif #endif
#define HOMING_CYCLE_0 (1 << X_AXIS) // this 'bot only homes the X axis #define HOMING_CYCLE_0 bit(X_AXIS) // this 'bot only homes the X axis
#ifdef HOMING_CYCLE_1 #ifdef HOMING_CYCLE_1
#undef HOMING_CYCLE_1 #undef HOMING_CYCLE_1
#endif #endif
@@ -65,7 +63,6 @@
#endif #endif
#define REED_SW_PIN GPIO_NUM_17 #define REED_SW_PIN GPIO_NUM_17
#define LIMIT_MASK 0
#ifndef ENABLE_CONTROL_SW_DEBOUNCE #ifndef ENABLE_CONTROL_SW_DEBOUNCE
#define ENABLE_CONTROL_SW_DEBOUNCE #define ENABLE_CONTROL_SW_DEBOUNCE
@@ -121,9 +118,9 @@
#define DEFAULT_Y_MAX_RATE 5000.0 // mm/min #define DEFAULT_Y_MAX_RATE 5000.0 // mm/min
#define DEFAULT_Z_MAX_RATE 200000.0 // mm/min #define DEFAULT_Z_MAX_RATE 200000.0 // mm/min
#define DEFAULT_X_ACCELERATION (500.0 * 60 * 60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_ACCELERATION 500.0 // mm/sec^2. 500 mm/sec^2 = 1800000 mm/min^2
#define DEFAULT_Y_ACCELERATION (500.0 * 60 * 60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION 500.0 // mm/sec^2
#define DEFAULT_Z_ACCELERATION (500.0 * 60 * 60) #define DEFAULT_Z_ACCELERATION 500.0 // mm/sec^2
#define DEFAULT_X_MAX_TRAVEL 120.0 // mm NOTE: Must be a positive value. #define DEFAULT_X_MAX_TRAVEL 120.0 // mm NOTE: Must be a positive value.
#define DEFAULT_Y_MAX_TRAVEL 20000.0 // mm NOTE: Must be a positive value. #define DEFAULT_Y_MAX_TRAVEL 20000.0 // mm NOTE: Must be a positive value.

View File

@@ -0,0 +1,296 @@
/*
esp32_printer_controller.h
Part of Grbl_ESP32
Template for a machine configuration file.
2020 - Mitch Bradley
2020 - Michiyasu Odaki
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
// This contains a long list of things that might possibly be
// configured. Most machines - especially simple cartesian machines
// that use stepper motors - will only need to define a few of the
// options herein, often just the pin assignments.
// Pin assignments depend on how the ESP32 is connected to
// the external machine. Typically the ESP32 module plugs into
// an adapter board that wires specific ESP32 GPIO pins to
// other connectors on the board, such as Pololu sockets for
// stepper drivers or connectors for external drivers, limit
// pins, spindle control, etc. This file describes how those
// GPIO pins are wired to those other connectors.
// Some machines might choose to use an adapter board in a
// non-standard way, for example a 3-axis board might have axes
// labeled XYZ, but the machine might have only 2 axes one of which is
// driven by two ganged motors. In that case, you would need
// a custom version of this file that assigns the pins differently
// from the adapter board labels.
// In addition to pin assignments, many other aspects of Grbl
// can be configured, such as spindle speeds, special motor
// types like servos and unipolars, homing order, default values
// for $$ settings, etc. A detailed list of such options is
// given below.
// Furthermore, it is possible to implement special complex
// behavior in custom C++ code, for non-Cartesian machines,
// unusual homing cycles, etc. See the Special Features section
// below for additional instructions.
// 3D printer controller using ESP32 processor
// https://github.com/MitchBradley/Esp32PrinterController
// === Machine Name
// Change TEMPLATE to some name of your own choosing. That name
// will be shown in a Grbl startup message to identify your
// configuration.
#define MACHINE_NAME "ESP32_PRINTER_CONTROLLER"
// If your machine requires custom code as described below in
// Special Features, you must copy Custom/custom_code_template.cpp
// to a new name like Custom/my_custom_code.cpp, implement the
// functions therein, and enable its use by defining:
#define CUSTOM_CODE_FILENAME "Custom/esp32_printer_controller.cpp"
// === Number of axes
// You can set the number of axes that the machine supports
// by defining N_AXIS. If you do not define it, 3 will be
// used. The value must be at least 3, even if your machine
// has fewer axes.
#define N_AXIS 3
// == Pin Assignments
// Step and direction pins; these must be output-capable pins,
// specifically ESP32 GPIO numbers 0..31
// With the I2S I/O expander enabled, you can specify 128..159 as output pins.
#define X_STEP_PIN I2SO(9)
#define X_DIRECTION_PIN I2SO(7)
#define Y_STEP_PIN I2SO(5)
#define Y_DIRECTION_PIN I2SO(4)
#define Z_STEP_PIN I2SO(2)
#define Z_DIRECTION_PIN I2SO(1)
#define A_STEP_PIN I2SO(12)
#define A_DIRECTION_PIN I2SO(13)
#define X_LIMIT_PIN GPIO_NUM_34
//#define Y_LIMIT_PIN GPIO_NUM_35
//#define Z_LIMIT_PIN GPIO_NUM_32
// Common enable for all steppers. If it is okay to leave
// your drivers enabled at all times, you can leave
// STEPPERS_DISABLE_PIN undefined and use the pin for something else.
// #define STEPPERS_DISABLE_PIN GPIO_NUM_13
// Pins for controlling various aspects of the machine. If your
// machine does not support one of these features, you can leave
// the corresponding pin undefined.
#define SPINDLE_TYPE SPINDLE_TYPE_NONE
// #define SPINDLE_PWM_PIN GPIO_NUM_2 // labeled SpinPWM
// #define SPINDLE_ENABLE_PIN GPIO_NUM_22 // labeled SpinEnbl
// #define COOLANT_MIST_PIN GPIO_NUM_21 // labeled Mist
// #define COOLANT_FLOOD_PIN GPIO_NUM_25 // labeled Flood
// #define PROBE_PIN GPIO_NUM_32 // labeled Probe
// Input pins for various functions. If the corresponding pin is not defined,
// the function will not be available.
// CONTROL_SAFETY_DOOR_PIN shuts off the machine when a door is opened
// or some other unsafe condition exists.
// #define CONTROL_SAFETY_DOOR_PIN GPIO_NUM_35 // labeled Door, needs external pullup
// RESET, FEED_HOLD, and CYCLE_START can control GCode execution at
// the push of a button.
// #define CONTROL_FEED_HOLD_PIN GPIO_NUM_36 // labeled Hold, needs external pullup
// #define CONTROL_CYCLE_START_PIN GPIO_NUM_39 // labeled Start, needs external pullup
// === Ganging
// If you need to use two motors on one axis, you can "gang" the motors by
// defining a second pin to control the other motor on the axis. For example:
// #define Y2_STEP_PIN GPIO_NUM_27 /* labeled Z */
// #define Y2_DIRECTION_PIN GPIO_NUM_33 /* labeled Z */
// === Servos
// To use a servo motor on an axis, do not define step and direction
// pins for that axis, but instead include a block like this:
//#define USE_SERVO_AXES
//#define SERVO_Z_PIN GPIO_NUM_15 // It cannot be used when JTAG debugging
//#define SERVO_Z_RANGE_MIN 0.0
//#define SERVO_Z_RANGE_MAX 5.0
//#define SERVO_Z_HOMING_TYPE SERVO_HOMING_TARGET // during homing it will instantly move to a target value
//#define SERVO_Z_HOME_POS SERVO_Z_RANGE_MAX // move to max during homing
//#define SERVO_Z_MPOS false // will not use mpos, uses work coordinates
// === Homing cycles
// The default homing order is Z first (HOMING_CYCLE_0),
// then X (HOMING_CYCLE_1), and finally Y (HOMING_CYCLE_2)
// For machines that need different homing order, you can
// undefine HOMING_CYCLE_n and redefine it accordingly.
// For example, the following would first home X and Y
// simultaneously, then A and B simultaneously, and Z
// not at all.
// redefine some stuff from config.h
#ifdef HOMING_CYCLE_0
#undef HOMING_CYCLE_0
#endif
#define HOMING_CYCLE_0 bit(X_AXIS) // this 'bot only homes the X axis
#ifdef HOMING_CYCLE_1
#undef HOMING_CYCLE_1
#endif
#ifdef HOMING_CYCLE_2
#undef HOMING_CYCLE_2
#endif
// === Default settings
// Grbl has many run-time settings that the user can changed by
// commands like $110=2000 . Their values are stored in EEPROM
// so they persist after the controller has been powered down.
// Those settings have default values that are used if the user
// has not altered them, or if the settings are explicitly reset
// to the default values wth $RST=$.
//
// The default values are established in defaults.h, but you
// can override any one of them by definining it here, for example:
//#define DEFAULT_INVERT_LIMIT_PINS 1
//#define DEFAULT_REPORT_INCHES 1
#define DEFAULT_STEP_PULSE_MICROSECONDS I2S_OUT_USEC_PER_PULSE
#define DEFAULT_HOMING_ENABLE 1
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir Z, negative X,Y
#define DEFAULT_HOMING_FEED_RATE 200.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 1000.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 3.0 // mm
#define DEFAULT_HARD_LIMIT_ENABLE 1
// === Control Pins
// The control pins with names that begin with CONTROL_ are
// ignored by default, to avoid noise problems. To make them
// work, you must undefine IGNORE_CONTROL_PINS
#undef IGNORE_CONTROL_PINS
// If some of the control pin switches are normally closed
// (the default is normally open), you can invert some of them
// with INVERT_CONTROL_PIN_MASK. The bits in the mask are
// Cycle Start, Feed Hold, Reset, Safety Door. To use a
// normally open switch on Reset, you would say
// #define INVERT_CONTROL_PIN_MASK B1101
// If your control pins do not have adequate hardware signal
// conditioning, you can define these to use software to
// reduce false triggering.
// #define ENABLE_CONTROL_SW_DEBOUNCE // Default disabled. Uncomment to enable.
// #define CONTROL_SW_DEBOUNCE_PERIOD 32 // in milliseconds default 32 microseconds
#ifndef ENABLE_CONTROL_SW_DEBOUNCE
#define ENABLE_CONTROL_SW_DEBOUNCE
#endif
#ifdef CONTROL_SW_DEBOUNCE_PERIOD
#undef CONTROL_SW_DEBOUNCE_PERIOD
#endif
#define CONTROL_SW_DEBOUNCE_PERIOD 100 // really long debounce
#ifdef INVERT_CONTROL_PIN_MASK
#undef INVERT_CONTROL_PIN_MASK
#endif
#define INVERT_CONTROL_PIN_MASK B11111111
// Grbl_ESP32 use the ESP32's special RMT (IR remote control) hardware
// engine to achieve more precise high step rates than can be done
// in software. That feature is enabled by default, but there are
// some machines that might not want to use it, such as machines that
// do not use ordinary stepper motors. To turn it off, do this:
#undef USE_RMT_STEPS
//
// I2S (steppers & other output-only pins)
//
#define USE_I2S_OUT
#define I2S_OUT_BCK GPIO_NUM_22
#define I2S_OUT_WS GPIO_NUM_17
#define I2S_OUT_DATA GPIO_NUM_21
// Define USE_I2S_OUT_STREAM if buffering is used.
// (there will be a delay between the specified I/O operation and the actual I/O execution)
#define I2S_OUT_NUM_BITS 16
#define USE_I2S_OUT_STREAM
// === Special Features
// Grbl_ESP32 can support non-Cartesian machines and some other
// scenarios that cannot be handled by choosing from a set of
// predefined selections. Instead they require machine-specific
// C++ code functions. There are callouts in the core code for
// such code, guarded by ifdefs that enable calling the individual
// functions. custom_code_template.cpp describes the functions
// that you can implement. The ifdef guards are described below:
//
#define USE_MACHINE_INIT
// USE_CUSTOM_HOMING enables the user_defined_homing() function
// that can implement an arbitrary homing sequence.
// #define USE_CUSTOM_HOMING
// USE_KINEMATICS enables the functions inverse_kinematics(),
// kinematics_pre_homing(), and kinematics_post_homing(),
// so non-Cartesian machines can be implemented.
// #define USE_KINEMATICS
// USE_FWD_KINEMATIC enables the forward_kinematics() function
// that converts motor positions in non-Cartesian coordinate
// systems back to Cartesian form, for status reports.
//#define USE_FWD_KINEMATIC
// USE_TOOL_CHANGE enables the user_tool_change() function
// that implements custom tool change procedures.
// #define USE_TOOL_CHANGE
// Any one of MACRO_BUTTON_0_PIN, MACRO_BUTTON_1_PIN, and MACRO_BUTTON_2_PIN
// enables the user_defined_macro(number) function which
// implements custom behavior at the press of a button
// #define MACRO_BUTTON_0_PIN
// USE_M30 enables the user_m30() function which implements
// custom behavior when a GCode programs stops at the end
// #define USE_M30
// USE_TRIAMINIC enables a suite of functions that are defined
// in grbl_triaminic.cpp, allowing the use of Triaminic stepper
// drivers that require software configuration at startup.
// There are several options that control the details of such
// drivers; inspect the code in grbl_triaminic.cpp to see them.
// #define USE_TRIAMINIC
// #define X_TRIAMINIC
// #define X_DRIVER_TMC2209
// #define TRIAMINIC_DAISY_CHAIN
// USE_MACHINE_TRINAMIC_INIT enables the machine_triaminic_setup()
// function that replaces the normal setup of Triaminic drivers.
// It could, for, example, setup StallGuard or other special modes.
// #define USE_MACHINE_TRINAMIC_INIT
// === Grbl behavior options
// There are quite a few options that control aspects of Grbl that
// are not specific to particular machines. They are listed and
// described in config.h after it includes the file machine.h.
// Normally you would not need to change them, but if you do,
// it will be necessary to make the change in config.h

View File

@@ -50,7 +50,6 @@
#define X_LIMIT_PIN GPIO_NUM_13 #define X_LIMIT_PIN GPIO_NUM_13
#define Y_LIMIT_PIN GPIO_NUM_5 #define Y_LIMIT_PIN GPIO_NUM_5
#define Z_LIMIT_PIN GPIO_NUM_19 #define Z_LIMIT_PIN GPIO_NUM_19
#define LIMIT_MASK B111
#define PROBE_PIN GPIO_NUM_39 #define PROBE_PIN GPIO_NUM_39

View File

@@ -69,7 +69,6 @@
#define Y_LIMIT_PIN GPIO_NUM_17 #define Y_LIMIT_PIN GPIO_NUM_17
#define A_LIMIT_PIN GPIO_NUM_16 #define A_LIMIT_PIN GPIO_NUM_16
#define B_LIMIT_PIN GPIO_NUM_4 #define B_LIMIT_PIN GPIO_NUM_4
#define LIMIT_MASK B11011
// OK to comment out to use pin for other features // OK to comment out to use pin for other features
#define STEPPERS_DISABLE_PIN GPIO_NUM_13 #define STEPPERS_DISABLE_PIN GPIO_NUM_13
@@ -77,26 +76,26 @@
#ifdef HOMING_CYCLE_0 // undefine from config.h #ifdef HOMING_CYCLE_0 // undefine from config.h
#undef HOMING_CYCLE_0 #undef HOMING_CYCLE_0
#endif #endif
//#define HOMING_CYCLE_0 (1<<X_AXIS) //#define HOMING_CYCLE_0 bit(X_AXIS)
#define HOMING_CYCLE_0 ((1<<X_AXIS)|(1<<Y_AXIS)) #define HOMING_CYCLE_0 (bit(X_AXIS)|bit(Y_AXIS))
//#define HOMING_CYCLE_0 ((1<<X_AXIS)|(1<<Y_AXIS) |(1<<A_AXIS)|(1<<B_AXIS)) //#define HOMING_CYCLE_0 (bit(X_AXIS)|bit(Y_AXIS) |bit(A_AXIS)|bit(B_AXIS))
#ifdef HOMING_CYCLE_1 // undefine from config.h #ifdef HOMING_CYCLE_1 // undefine from config.h
#undef HOMING_CYCLE_1 #undef HOMING_CYCLE_1
#endif #endif
//#define HOMING_CYCLE_1 (1<<A_AXIS) //#define HOMING_CYCLE_1 bit(A_AXIS)
#define HOMING_CYCLE_1 ((1<<A_AXIS)|(1<<B_AXIS)) #define HOMING_CYCLE_1 (bit(A_AXIS)|bit(B_AXIS))
#ifdef HOMING_CYCLE_2 // undefine from config.h #ifdef HOMING_CYCLE_2 // undefine from config.h
#undef HOMING_CYCLE_2 #undef HOMING_CYCLE_2
#endif #endif
/* /*
#define HOMING_CYCLE_2 (1<<Y_AXIS) #define HOMING_CYCLE_2 bit(Y_AXIS)
#ifdef HOMING_CYCLE_3 // undefine from config.h #ifdef HOMING_CYCLE_3 // undefine from config.h
#undef HOMING_CYCLE_3 #undef HOMING_CYCLE_3
#endif #endif
#define HOMING_CYCLE_3 (1<<B_AXIS) #define HOMING_CYCLE_3 bit(B_AXIS)
*/ */
#define DEFAULT_STEP_PULSE_MICROSECONDS 3 #define DEFAULT_STEP_PULSE_MICROSECONDS 3
@@ -139,12 +138,12 @@
#define DEFAULT_B_MAX_RATE 30000.0 // mm/min #define DEFAULT_B_MAX_RATE 30000.0 // mm/min
#define DEFAULT_C_MAX_RATE 8000.0 // mm/min #define DEFAULT_C_MAX_RATE 8000.0 // mm/min
#define DEFAULT_X_ACCELERATION (1500.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_ACCELERATION 1500.0 // mm/sec^2
#define DEFAULT_Y_ACCELERATION (1500.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION 1500.0 // mm/sec^2
#define DEFAULT_Z_ACCELERATION (100.0*60*60) #define DEFAULT_Z_ACCELERATION 100.0 // mm/sec^2
#define DEFAULT_A_ACCELERATION (1500.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_A_ACCELERATION 1500.0 // mm/sec^2
#define DEFAULT_B_ACCELERATION (1500.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_B_ACCELERATION 1500.0 // mm/sec^2
#define DEFAULT_C_ACCELERATION (100.0*60*60) #define DEFAULT_C_ACCELERATION 100.0 // mm/sec^2
#define DEFAULT_X_MAX_TRAVEL 250.0 // mm NOTE: Must be a positive value. #define DEFAULT_X_MAX_TRAVEL 250.0 // mm NOTE: Must be a positive value.
#define DEFAULT_Y_MAX_TRAVEL 250.0 // mm NOTE: Must be a positive value. #define DEFAULT_Y_MAX_TRAVEL 250.0 // mm NOTE: Must be a positive value.

View File

@@ -0,0 +1,104 @@
/*
i2s_out_xxyyzz.h
Part of Grbl_ESP32
Pin assignments for the ESP32 I2S 6-axis board
2018 - Bart Dring
2020 - Mitch Bradley
2020 - Michiyasu Odaki
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
#define MACHINE_NAME "ESP32 I2S XXYYZZ Axis Driver Board (StepStick)"
#ifdef N_AXIS
#undef N_AXIS
#endif
#define N_AXIS 3
#ifdef ENABLE_SD_CARD
#undef ENABLE_SD_CARD
#endif
// === Special Features
// I2S (steppers & other output-only pins)
#define USE_I2S_OUT
// Define USE_I2S_OUT_STREAM if buffering is used.
// (there will be a delay between the specified I/O operation and the actual I/O execution)
#define USE_I2S_OUT_STREAM
#undef USE_RMT_STEPS
#define USE_STEPSTICK // makes sure MS1,2,3 !reset and !sleep are set
#define I2S_OUT_BCK GPIO_NUM_22
#define I2S_OUT_WS GPIO_NUM_17
#define I2S_OUT_DATA GPIO_NUM_21
#define STEPPER_MS1 GPIO_NUM_23 // MOSI
#define STEPPER_MS2 GPIO_NUM_18 // SCK
#define STEPPER_X_MS3 I2SO(3) // X_CS
#define STEPPER_Y_MS3 I2SO(6) // Y_CS
#define STEPPER_Z_MS3 I2SO(11) // Z_CS
#define STEPPER_A_MS3 I2SO(14) // A_CS
#define STEPPER_B_MS3 I2SO(19) // B_CS
#define STEPPER_C_MS3 I2SO(22) // C_CS
#define STEPPER_RESET GPIO_NUM_19
#define USE_GANGED_AXES // allow two motors on an axis
#define X_DISABLE_PIN I2SO(0)
#define X_DIRECTION_PIN I2SO(1)
#define X_STEP_PIN I2SO(2)
#define X2_DIRECTION_PIN I2SO(4)
#define X2_STEP_PIN I2SO(5)
#define X2_DISABLE_PIN I2SO(7)
#define Y_DISABLE_PIN I2SO(8)
#define Y_DIRECTION_PIN I2SO(9)
#define Y_STEP_PIN I2SO(10)
#define Y2_DIRECTION_PIN I2SO(12)
#define Y2_STEP_PIN I2SO(13)
#define Y2_DISABLE_PIN I2SO(15)
#define Z_DISABLE_PIN I2SO(16)
#define Z_DIRECTION_PIN I2SO(17)
#define Z_STEP_PIN I2SO(18)
#define Z2_DIRECTION_PIN I2SO(20)
#define Z2_STEP_PIN I2SO(21)
#define Z2_DISABLE_PIN I2SO(23)
#define SPINDLE_TYPE SPINDLE_TYPE_PWM // only one spindle at a time
#define SPINDLE_OUTPUT_PIN GPIO_NUM_26
#define SPINDLE_ENABLE_PIN GPIO_NUM_4
#define SPINDLE_DIR_PIN GPIO_NUM_16
#define X_LIMIT_PIN GPIO_NUM_36
#define Y_LIMIT_PIN GPIO_NUM_39
#define Z_LIMIT_PIN GPIO_NUM_34
//#define A_LIMIT_PIN GPIO_NUM_35
//#define B_LIMIT_PIN GPIO_NUM_32
//#define C_LIMIT_PIN GPIO_NUM_33
#define PROBE_PIN GPIO_NUM_25
#define COOLANT_MIST_PIN GPIO_NUM_2
// === Default settings
#define DEFAULT_STEP_PULSE_MICROSECONDS I2S_OUT_USEC_PER_PULSE

View File

@@ -0,0 +1,104 @@
/*
i2s_out_xyzabc.h
Part of Grbl_ESP32
Pin assignments for the ESP32 I2S 6-axis board
2018 - Bart Dring
2020 - Mitch Bradley
2020 - Michiyasu Odaki
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
#define MACHINE_NAME "ESP32 I2S 6 Axis Driver Board (StepStick)"
#ifdef N_AXIS
#undef N_AXIS
#endif
#define N_AXIS 6
#ifdef ENABLE_SD_CARD
#undef ENABLE_SD_CARD
#endif
// === Special Features
// I2S (steppers & other output-only pins)
#define USE_I2S_OUT
// Define USE_I2S_OUT_STREAM if buffering is used.
// (there will be a delay between the specified I/O operation and the actual I/O execution)
#define USE_I2S_OUT_STREAM
#undef USE_RMT_STEPS
#define USE_STEPSTICK // makes sure MS1,2,3 !reset and !sleep are set
#define I2S_OUT_BCK GPIO_NUM_22
#define I2S_OUT_WS GPIO_NUM_17
#define I2S_OUT_DATA GPIO_NUM_21
#define STEPPER_MS1 GPIO_NUM_23 // MOSI
#define STEPPER_MS2 GPIO_NUM_18 // SCK
#define STEPPER_X_MS3 I2SO(3) // X_CS
#define STEPPER_Y_MS3 I2SO(6) // Y_CS
#define STEPPER_Z_MS3 I2SO(11) // Z_CS
#define STEPPER_A_MS3 I2SO(14) // A_CS
#define STEPPER_B_MS3 I2SO(19) // B_CS
#define STEPPER_C_MS3 I2SO(22) // C_CS
#define STEPPER_RESET GPIO_NUM_19
#define X_DISABLE_PIN I2SO(0)
#define X_DIRECTION_PIN I2SO(1)
#define X_STEP_PIN I2SO(2)
#define Y_DIRECTION_PIN I2SO(4)
#define Y_STEP_PIN I2SO(5)
#define Y_DISABLE_PIN I2SO(7)
#define Z_DISABLE_PIN I2SO(8)
#define Z_DIRECTION_PIN I2SO(9)
#define Z_STEP_PIN I2SO(10)
#define A_DIRECTION_PIN I2SO(12)
#define A_STEP_PIN I2SO(13)
#define A_DISABLE_PIN I2SO(15)
#define B_DISABLE_PIN I2SO(16)
#define B_DIRECTION_PIN I2SO(17)
#define B_STEP_PIN I2SO(18)
//#define B_CS_PIN I2SO(19)
#define C_DIRECTION_PIN I2SO(20)
#define C_STEP_PIN I2SO(21)
//#define C_CS_PIN I2SO(22)
#define C_DISABLE_PIN I2SO(23)
#define SPINDLE_TYPE SPINDLE_TYPE_PWM // only one spindle at a time
#define SPINDLE_OUTPUT_PIN GPIO_NUM_26
#define SPINDLE_ENABLE_PIN GPIO_NUM_4
#define SPINDLE_DIR_PIN GPIO_NUM_16
#define X_LIMIT_PIN GPIO_NUM_36
#define Y_LIMIT_PIN GPIO_NUM_39
#define Z_LIMIT_PIN GPIO_NUM_34
#define A_LIMIT_PIN GPIO_NUM_35
#define B_LIMIT_PIN GPIO_NUM_32
#define C_LIMIT_PIN GPIO_NUM_33
#define PROBE_PIN GPIO_NUM_25
#define COOLANT_MIST_PIN GPIO_NUM_2
// === Default settings
#define DEFAULT_STEP_PULSE_MICROSECONDS I2S_OUT_USEC_PER_PULSE

View File

@@ -0,0 +1,105 @@
/*
i2s_out_xyzabc_trinamic.h
Part of Grbl_ESP32
Pin assignments for the ESP32 SPI 6-axis board
2018 - Bart Dring
2020 - Mitch Bradley
2020 - Michiyasu Odaki
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
#define MACHINE_NAME "ESP32 SPI 6 Axis Driver Board Trinamic"
#ifdef N_AXIS
#undef N_AXIS
#endif
#define N_AXIS 6
// === Special Features
// I2S (steppers & other output-only pins)
#define USE_I2S_OUT
// Define USE_I2S_OUT_STREAM if buffering is used.
// (there will be a delay between the specified I/O operation and the actual I/O execution)
#define USE_I2S_OUT_STREAM
#undef USE_RMT_STEPS
#define I2S_OUT_BCK GPIO_NUM_22
#define I2S_OUT_WS GPIO_NUM_17
#define I2S_OUT_DATA GPIO_NUM_21
#define TRINAMIC_RUN_MODE TRINAMIC_MODE_COOLSTEP
#define TRINAMIC_HOMING_MODE TRINAMIC_MODE_COOLSTEP
#define X_TRINAMIC_DRIVER 2130
#define X_DISABLE_PIN I2SO(0)
#define X_DIRECTION_PIN I2SO(1)
#define X_STEP_PIN I2SO(2)
#define X_CS_PIN I2SO(3)
#define X_RSENSE TMC2130_RSENSE_DEFAULT
#define Y_TRINAMIC_DRIVER 2130
#define Y_DIRECTION_PIN I2SO(4)
#define Y_STEP_PIN I2SO(5)
#define Y_DISABLE_PIN I2SO(7)
#define Y_CS_PIN I2SO(6)
#define Y_RSENSE X_RSENSE
#define Z_TRINAMIC_DRIVER 2130
#define Z_DISABLE_PIN I2SO(8)
#define Z_DIRECTION_PIN I2SO(9)
#define Z_STEP_PIN I2SO(10)
#define Z_CS_PIN I2SO(11)
#define Z_RSENSE X_RSENSE
#define A_TRINAMIC_DRIVER 2130
#define A_DIRECTION_PIN I2SO(12)
#define A_STEP_PIN I2SO(13)
#define A_DISABLE_PIN I2SO(15)
#define A_CS_PIN I2SO(14)
#define A_RSENSE X_RSENSE
#define B_TRINAMIC_DRIVER 2130
#define B_DISABLE_PIN I2SO(16)
#define B_DIRECTION_PIN I2SO(17)
#define B_STEP_PIN I2SO(18)
#define B_CS_PIN I2SO(19)
#define B_RSENSE X_RSENSE
#define C_TRINAMIC_DRIVER 2130
#define C_DIRECTION_PIN I2SO(20)
#define C_STEP_PIN I2SO(21)
#define C_DISABLE_PIN I2SO(23)
#define C_CS_PIN I2SO(22)
#define C_RSENSE X_RSENSE
/*
#define SPINDLE_TYPE SPINDLE_TYPE_PWM // only one spindle at a time
#define SPINDLE_OUTPUT_PIN GPIO_NUM_26
#define SPINDLE_ENABLE_PIN GPIO_NUM_4
#define SPINDLE_DIR_PIN GPIO_NUM_16
*/
#define X_LIMIT_PIN GPIO_NUM_33
#define Y_LIMIT_PIN GPIO_NUM_32
#define Z_LIMIT_PIN GPIO_NUM_35
#define A_LIMIT_PIN GPIO_NUM_34
#define B_LIMIT_PIN GPIO_NUM_39
#define C_LIMIT_PIN GPIO_NUM_36
#define SPINDLE_TYPE SPINDLE_TYPE_RELAY
#define SPINDLE_OUTPUT_PIN GPIO_NUM_26
#define PROBE_PIN GPIO_NUM_25
#define COOLANT_MIST_PIN GPIO_NUM_2
// === Default settings
#define DEFAULT_STEP_PULSE_MICROSECONDS I2S_OUT_USEC_PER_PULSE

View File

@@ -1,25 +1,20 @@
/* /*
lowrider.h lowrider_v1p2.h
Part of Grbl_ESP32 Part of Grbl_ESP32
Pin assignments for the Buildlog.net MPCNC controller Pin assignments for the Buildlog.net MPCNC controller
used in lowrider mode. Low rider has (2) Y and Z and one X motor used in lowrider mode. Low rider has (2) Y and Z and one X motor
These will not match the silkscreen or schematic descriptions...see definitions below These will not match the silkscreen or schematic descriptions...see definitions below
https://github.com/bdring/Grbl_ESP32_MPCNC_Controller https://github.com/bdring/Grbl_ESP32_MPCNC_Controller
2019 - Bart Dring 2019 - Bart Dring
2020 - Mitch Bradley 2020 - Mitch Bradley
Grbl_ESP32 is free software: you can redistribute it and/or modify Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Grbl is distributed in the hope that it will be useful, Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>. along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/ */
@@ -33,15 +28,18 @@
#define Y_STEP_PIN GPIO_NUM_27 // use Z labeled connector #define Y_STEP_PIN GPIO_NUM_27 // use Z labeled connector
#define Y_DIRECTION_PIN GPIO_NUM_33 // use Z labeled connector #define Y_DIRECTION_PIN GPIO_NUM_33 // use Z labeled connector
#define Z_STEP_PIN GPIO_NUM_14 // use Y labeled connector #define Z_STEP_PIN GPIO_NUM_12 // use X labeled connector
#define Z2_STEP_PIN GPIO_NUM_21 // ganged motor #define Z2_STEP_PIN GPIO_NUM_22 // use X labeled connector
#define Z_DIRECTION_PIN GPIO_NUM_25 // use Y labeled connector #define Z_DIRECTION_PIN GPIO_NUM_26 // use X labeled connector
#define Z2_DIRECTION_PIN Z_DIRECTION_PIN
#define Z_AXIS_SQUARING #define Z_AXIS_SQUARING
#define X_STEP_PIN GPIO_NUM_12 // use X labeled connector #define X_STEP_PIN GPIO_NUM_12 // use X labeled connector
#define X2_STEP_PIN GPIO_NUM_22 // ganged motor #define X2_STEP_PIN GPIO_NUM_22 // ganged motor
#define X_DIRECTION_PIN GPIO_NUM_26 // use X labeled connector #define X_DIRECTION_PIN GPIO_NUM_26 // use X labeled connector
#define X_AXIS_SQUARING #define X2_DIRECTION_PIN X_DIRECTION_PIN
#define X_AXIS_SQUARING
// OK to comment out to use pin for other features // OK to comment out to use pin for other features
#define STEPPERS_DISABLE_PIN GPIO_NUM_13 #define STEPPERS_DISABLE_PIN GPIO_NUM_13
@@ -66,8 +64,6 @@
#define Z_LIMIT_PIN GPIO_NUM_4 #define Z_LIMIT_PIN GPIO_NUM_4
#define X_LIMIT_PIN GPIO_NUM_17 #define X_LIMIT_PIN GPIO_NUM_17
#define LIMIT_MASK B111
#ifndef ENABLE_SOFTWARE_DEBOUNCE // V1P2 does not have R/C filters #ifndef ENABLE_SOFTWARE_DEBOUNCE // V1P2 does not have R/C filters
#define ENABLE_SOFTWARE_DEBOUNCE #define ENABLE_SOFTWARE_DEBOUNCE
#endif #endif

View File

@@ -22,7 +22,7 @@
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>. along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define MACHINE_NAME "midTbot" #define MACHINE_NAME "MIDTBOT"
#define SPINDLE_TYPE SPINDLE_TYPE_NONE #define SPINDLE_TYPE SPINDLE_TYPE_NONE
@@ -40,29 +40,23 @@
#define X_LIMIT_PIN GPIO_NUM_2 #define X_LIMIT_PIN GPIO_NUM_2
#define Y_LIMIT_PIN GPIO_NUM_4 #define Y_LIMIT_PIN GPIO_NUM_4
#define LIMIT_MASK B11
#define USE_SERVO_AXES #define Z_SERVO_PIN GPIO_NUM_27
#define Z_SERVO_RANGE_MIN 0.0
#define SERVO_Z_PIN GPIO_NUM_27 #define Z_SERVO_RANGE_MAX 5.0
#define SERVO_Z_RANGE_MIN 0.0
#define SERVO_Z_RANGE_MAX 5.0
#define SERVO_Z_HOMING_TYPE SERVO_HOMING_TARGET // during homing it will instantly move to a target value
#define SERVO_Z_HOME_POS SERVO_Z_RANGE_MAX // move to max during homing
#define SERVO_Z_MPOS false // will not use mpos, uses work coordinates
// redefine some stuff from config.h // redefine some stuff from config.h
#ifdef HOMING_CYCLE_0 #ifdef HOMING_CYCLE_0
#undef HOMING_CYCLE_0 #undef HOMING_CYCLE_0
#endif #endif
#define HOMING_CYCLE_0 (1<<Y_AXIS) #define HOMING_CYCLE_0 bit(Y_AXIS)
#ifdef HOMING_CYCLE_1 #ifdef HOMING_CYCLE_1
#undef HOMING_CYCLE_1 #undef HOMING_CYCLE_1
#endif #endif
#define HOMING_CYCLE_1 (1<<X_AXIS) #define HOMING_CYCLE_1 bit(X_AXIS)
#ifdef HOMING_CYCLE_2 #ifdef HOMING_CYCLE_2
#undef HOMING_CYCLE_2 #undef HOMING_CYCLE_2
@@ -106,9 +100,9 @@
#define DEFAULT_Y_MAX_RATE 8000.0 // mm/min #define DEFAULT_Y_MAX_RATE 8000.0 // mm/min
#define DEFAULT_Z_MAX_RATE 5000.0 // mm/min #define DEFAULT_Z_MAX_RATE 5000.0 // mm/min
#define DEFAULT_X_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_ACCELERATION 200.0 // mm/sec^2. 200 mm/sec^2 = 720000 mm/min^2
#define DEFAULT_Y_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION 200.0 // mm/sec^2
#define DEFAULT_Z_ACCELERATION (100.0*60*60) #define DEFAULT_Z_ACCELERATION 100.0 // mm/sec^2
#define DEFAULT_X_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value. #define DEFAULT_X_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value.
#define DEFAULT_Y_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value. #define DEFAULT_Y_MAX_TRAVEL 100.0 // mm NOTE: Must be a positive value.

View File

@@ -40,7 +40,9 @@
#define Z_STEP_PIN GPIO_NUM_27 #define Z_STEP_PIN GPIO_NUM_27
#define X_DIRECTION_PIN GPIO_NUM_26 #define X_DIRECTION_PIN GPIO_NUM_26
#define X2_DIRECTION_PIN X_DIRECTION_PIN
#define Y_DIRECTION_PIN GPIO_NUM_25 #define Y_DIRECTION_PIN GPIO_NUM_25
#define Y2_DIRECTION_PIN Y_DIRECTION_PIN
#define Z_DIRECTION_PIN GPIO_NUM_33 #define Z_DIRECTION_PIN GPIO_NUM_33
// OK to comment out to use pin for other features // OK to comment out to use pin for other features
@@ -68,7 +70,6 @@
#define X_LIMIT_PIN GPIO_NUM_2 #define X_LIMIT_PIN GPIO_NUM_2
#define Y_LIMIT_PIN GPIO_NUM_4 #define Y_LIMIT_PIN GPIO_NUM_4
#define Z_LIMIT_PIN GPIO_NUM_15 #define Z_LIMIT_PIN GPIO_NUM_15
#define LIMIT_MASK B111
#define PROBE_PIN GPIO_NUM_35 #define PROBE_PIN GPIO_NUM_35
@@ -126,9 +127,9 @@
#define DEFAULT_Y_MAX_RATE 8000.0 // mm/min #define DEFAULT_Y_MAX_RATE 8000.0 // mm/min
#define DEFAULT_Z_MAX_RATE 3000.0 // mm/min #define DEFAULT_Z_MAX_RATE 3000.0 // mm/min
#define DEFAULT_X_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_ACCELERATION 200.0 // mm/sec^2
#define DEFAULT_Y_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION 200.0 // mm/sec^2
#define DEFAULT_Z_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Z_ACCELERATION 100.0 // mm/sec^2
#define DEFAULT_X_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value. #define DEFAULT_X_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value.
#define DEFAULT_Y_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value. #define DEFAULT_Y_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value.

View File

@@ -1,5 +1,5 @@
/* /*
mpcnc.h mpcnc_v1p2.h
Part of Grbl_ESP32 Part of Grbl_ESP32
Pin assignments for the Buildlog.net MPCNC controller Pin assignments for the Buildlog.net MPCNC controller
@@ -41,7 +41,9 @@
#define Z_STEP_PIN GPIO_NUM_27 #define Z_STEP_PIN GPIO_NUM_27
#define X_DIRECTION_PIN GPIO_NUM_26 #define X_DIRECTION_PIN GPIO_NUM_26
#define X2_DIRECTION_PIN X_DIRECTION_PIN
#define Y_DIRECTION_PIN GPIO_NUM_25 #define Y_DIRECTION_PIN GPIO_NUM_25
#define Y2_DIRECTION_PIN Y_DIRECTION_PIN
#define Z_DIRECTION_PIN GPIO_NUM_33 #define Z_DIRECTION_PIN GPIO_NUM_33
// OK to comment out to use pin for other features // OK to comment out to use pin for other features
@@ -69,8 +71,6 @@
#define X_LIMIT_PIN GPIO_NUM_17 #define X_LIMIT_PIN GPIO_NUM_17
#define Y_LIMIT_PIN GPIO_NUM_4 #define Y_LIMIT_PIN GPIO_NUM_4
#define Z_LIMIT_PIN GPIO_NUM_15 #define Z_LIMIT_PIN GPIO_NUM_15
#define LIMIT_MASK B111
#ifndef ENABLE_SOFTWARE_DEBOUNCE // V1P2 does not have R/C filters #ifndef ENABLE_SOFTWARE_DEBOUNCE // V1P2 does not have R/C filters
#define ENABLE_SOFTWARE_DEBOUNCE #define ENABLE_SOFTWARE_DEBOUNCE
@@ -134,9 +134,9 @@
#define DEFAULT_Y_MAX_RATE 8000.0 // mm/min #define DEFAULT_Y_MAX_RATE 8000.0 // mm/min
#define DEFAULT_Z_MAX_RATE 3000.0 // mm/min #define DEFAULT_Z_MAX_RATE 3000.0 // mm/min
#define DEFAULT_X_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_ACCELERATION 200.0 // mm/sec^2
#define DEFAULT_Y_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION 200.0 // mm/sec^2
#define DEFAULT_Z_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Z_ACCELERATION 100.0 // mm/sec^2
#define DEFAULT_X_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value. #define DEFAULT_X_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value.
#define DEFAULT_Y_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value. #define DEFAULT_Y_MAX_TRAVEL 500.0 // mm NOTE: Must be a positive value.

View File

@@ -47,16 +47,14 @@
#define X_LIMIT_PIN GPIO_NUM_15 #define X_LIMIT_PIN GPIO_NUM_15
#endif #endif
#define Y_LIMIT_PIN GPIO_NUM_4 #define Y_LIMIT_PIN GPIO_NUM_4
#define LIMIT_MASK B11
#define USING_SERVO // uncomment to use this feature #define USING_SERVO // uncomment to use this feature
//#define USING_SOLENOID // uncomment to use this feature //#define USING_SOLENOID // uncomment to use this feature
#ifdef USING_SERVO #ifdef USING_SERVO
#define USE_SERVO_AXES #define Z_SERVO_PIN GPIO_NUM_27
#define SERVO_Z_PIN GPIO_NUM_27 #define Z_SERVO_RANGE_MIN 0.0
#define SERVO_Z_RANGE_MIN 0.0 #define Z_SERVO_RANGE_MAX 10.0
#define SERVO_Z_RANGE_MAX 10.0
#endif #endif
#ifdef USING_SOLENOID #ifdef USING_SOLENOID
@@ -105,9 +103,9 @@
#define DEFAULT_Y_MAX_RATE 5000.0 // mm/min #define DEFAULT_Y_MAX_RATE 5000.0 // mm/min
#define DEFAULT_Z_MAX_RATE 5000.0 // mm/min #define DEFAULT_Z_MAX_RATE 5000.0 // mm/min
#define DEFAULT_X_ACCELERATION (50.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_ACCELERATION 50.0 // mm/sec^2. 50 mm/sec^2 = 180000 mm/min^2
#define DEFAULT_Y_ACCELERATION (50.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION 50.0 // mm/sec^2
#define DEFAULT_Z_ACCELERATION (50.0*60*60) #define DEFAULT_Z_ACCELERATION 50.0 // mm/sec^2
#define DEFAULT_X_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value. #define DEFAULT_X_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value.
#define DEFAULT_Y_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value. #define DEFAULT_Y_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value.

View File

@@ -22,7 +22,7 @@
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>. along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define MACHINE_NAME "Polar Coaster" #define MACHINE_NAME "POLAR_COASTER"
// This causes the custom code file to be included in the build // This causes the custom code file to be included in the build
// via ../custom_code.cpp // via ../custom_code.cpp
@@ -45,17 +45,11 @@
#define STEPPERS_DISABLE_PIN GPIO_NUM_17 #define STEPPERS_DISABLE_PIN GPIO_NUM_17
#define USE_SERVO_AXES #define Z_SERVO_PIN GPIO_NUM_16
#define Z_SERVO_RANGE_MIN 0.0
#define SERVO_Z_PIN GPIO_NUM_16 #define Z_SERVO_RANGE_MAX 5.0
#define SERVO_Z_RANGE_MIN 0.0
#define SERVO_Z_RANGE_MAX 5.0
#define SERVO_Z_HOMING_TYPE SERVO_HOMING_TARGET // during homing it will instantly move to a target value
#define SERVO_Z_HOME_POS SERVO_Z_RANGE_MAX // move to max during homing
#define SERVO_Z_MPOS false // will not use mpos, uses work coordinates
#define X_LIMIT_PIN GPIO_NUM_4 #define X_LIMIT_PIN GPIO_NUM_4
#define LIMIT_MASK B1
#define SPINDLE_TYPE SPINDLE_TYPE_NONE #define SPINDLE_TYPE SPINDLE_TYPE_NONE
@@ -81,7 +75,7 @@
#ifdef HOMING_CYCLE_0 #ifdef HOMING_CYCLE_0
#undef HOMING_CYCLE_0 #undef HOMING_CYCLE_0
#endif #endif
#define HOMING_CYCLE_0 (1<<X_AXIS) // this 'bot only homes the X axis #define HOMING_CYCLE_0 bit(X_AXIS) // this 'bot only homes the X axis
#ifdef HOMING_CYCLE_1 #ifdef HOMING_CYCLE_1
#undef HOMING_CYCLE_1 #undef HOMING_CYCLE_1
#endif #endif
@@ -130,9 +124,9 @@
#define DEFAULT_Y_MAX_RATE 15000.0 // mm/min #define DEFAULT_Y_MAX_RATE 15000.0 // mm/min
#define DEFAULT_Z_MAX_RATE 3000.0 // mm/min #define DEFAULT_Z_MAX_RATE 3000.0 // mm/min
#define DEFAULT_X_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_ACCELERATION 200.0 // mm/sec^2. 200 mm/sec^2 = 720000 mm/min^2
#define DEFAULT_Y_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION 200.0 // mm/sec^2
#define DEFAULT_Z_ACCELERATION (50.0*60*60) #define DEFAULT_Z_ACCELERATION 50.0 // mm/sec^2
#define DEFAULT_X_MAX_TRAVEL 50.0 // mm NOTE: Must be a positive value. #define DEFAULT_X_MAX_TRAVEL 50.0 // mm NOTE: Must be a positive value.
#define DEFAULT_Y_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value. #define DEFAULT_Y_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value.

View File

@@ -1,120 +0,0 @@
/*
servo_axis.h
Part of Grbl_ESP32
Pin assignments for the Buildlog.net pen laser controller V1
using servos.
For pen mode be sure to uncomment #define USE_PEN_SERVO in config.h
For solenoid mode be sure to uncomment #define USE_PEN_SERVO in config.h
For laser mode, you do not need to change anything
Note: You can use all 3 modes at the same time if you want
2018 - Bart Dring
2020 - Mitch Bradley
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
#define MACHINE_NAME "SERVO_AXIS"
// Pick a board version
//#define PEN_LASER_V1
#define PEN_LASER_V2
#define X_STEP_PIN GPIO_NUM_12
#define X_DIRECTION_PIN GPIO_NUM_26
#define STEPPERS_DISABLE_PIN GPIO_NUM_13
#ifdef PEN_LASER_V1
#define X_LIMIT_PIN GPIO_NUM_2
#endif
#ifdef PEN_LASER_V2
#define X_LIMIT_PIN GPIO_NUM_15
#endif
#define Y_LIMIT_PIN GPIO_NUM_4
#define LIMIT_MASK B11
// If SPINDLE_OUTPUT_PIN is commented out, this frees up the pin, but Grbl will still
// use a virtual spindle. Do not comment out the other parameters for the spindle.
#define SPINDLE_OUTPUT_PIN GPIO_NUM_17 // Laser PWM
// PWM Generator is based on 80,000,000 Hz counter
// Therefor the freq determines the resolution
// 80,000,000 / freq = max resolution
// For 5000 that is 80,000,000 / 5000 = 16000
// round down to nearest bit count for SPINDLE_PWM_MAX_VALUE
//#define SPINDLE_PWM_BASE_FREQ 5000 // Hz
#define SPINDLE_PWM_OFF_VALUE 0
#ifndef SPINDLE_PWM_MIN_VALUE
#define SPINDLE_PWM_MIN_VALUE 1 // Must be greater than zero.
#endif
#define SERVO_Y_PIN GPIO_NUM_14
#define SERVO_Y_RANGE_MIN 0.0
#define SERVO_Y_RANGE_MAX 30.0
#define SERVO_Z_PIN GPIO_NUM_27
#define SERVO_Z_RANGE_MIN 0.0
#define SERVO_Z_RANGE_MAX 20.0
// defaults
#define DEFAULT_STEP_PULSE_MICROSECONDS 3
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 250 // stay on
#define DEFAULT_STEPPING_INVERT_MASK 0 // uint8_t
#define DEFAULT_DIRECTION_INVERT_MASK 0 // uint8_t
#define DEFAULT_INVERT_ST_ENABLE 0 // boolean
#define DEFAULT_INVERT_LIMIT_PINS 1 // boolean
#define DEFAULT_INVERT_PROBE_PIN 0 // boolean
#define DEFAULT_STATUS_REPORT_MASK 1
#define DEFAULT_JUNCTION_DEVIATION 0.01 // mm
#define DEFAULT_ARC_TOLERANCE 0.002 // mm
#define DEFAULT_REPORT_INCHES 0 // false
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
#define DEFAULT_HARD_LIMIT_ENABLE 0 // false
#define DEFAULT_HOMING_ENABLE 0
#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir Z, negative X,Y
#define DEFAULT_HOMING_FEED_RATE 200.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 1000.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 3.0 // mm
#define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#define DEFAULT_LASER_MODE 0 // false
#define DEFAULT_X_STEPS_PER_MM 40 // half turn on a stepper
#define DEFAULT_Y_STEPS_PER_MM 100.0 // default calibration value
#define DEFAULT_Z_STEPS_PER_MM 100.0 // default calibration value
#define DEFAULT_X_MAX_RATE 2000.0 // mm/min
#define DEFAULT_Y_MAX_RATE 2000.0 // mm/min
#define DEFAULT_Z_MAX_RATE 2000.0 // mm/min
#define DEFAULT_X_ACCELERATION (50.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#define DEFAULT_Y_ACCELERATION (50.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#define DEFAULT_Z_ACCELERATION (50.0*60*60)
#define DEFAULT_X_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value.
#define DEFAULT_Y_MAX_TRAVEL 100.0 // default calibration value
#define DEFAULT_Z_MAX_TRAVEL 100.0 // default calibration value

View File

@@ -1,5 +1,5 @@
/* /*
spi_daisy_4axisxyyz.h spi_daisy_4axis_xyyz.h
Part of Grbl_ESP32 Part of Grbl_ESP32
Pin assignments for a 3-axis with Y ganged using Triaminic drivers Pin assignments for a 3-axis with Y ganged using Triaminic drivers
@@ -23,16 +23,18 @@
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>. along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define MACHINE_NAME "SPI Daisy 4x XYYZ" #define MACHINE_NAME "SPI_DAISY_4X_xyyz"
#ifdef N_AXIS #ifdef N_AXIS
#undef N_AXIS #undef N_AXIS
#endif #endif
#define N_AXIS 3 // can be 3 or 4. (if 3 install bypass jumper next to the A driver) #define N_AXIS 3 // can be 3 or 4. (if 3 install bypass jumper next to the A driver)
#define USE_TRINAMIC
#define TRINAMIC_DAISY_CHAIN #define TRINAMIC_DAISY_CHAIN
#define TRINAMIC_RUN_MODE TRINAMIC_MODE_COOLSTEP
#define TRINAMIC_HOMING_MODE TRINAMIC_MODE_COOLSTEP
// Use SPI enable instead of the enable pin // Use SPI enable instead of the enable pin
// The hardware enable pin is tied to ground // The hardware enable pin is tied to ground
#define USE_TRINAMIC_ENABLE #define USE_TRINAMIC_ENABLE
@@ -41,37 +43,33 @@
#define USE_GANGED_AXES #define USE_GANGED_AXES
// Y motor connects to the 1st driver // Y motor connects to the 1st driver
#define X_DRIVER_TMC2130 // Which Driver Type? #define X_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define X_RSENSE TMC2130_RSENSE_DEFAULT #define X_RSENSE TMC2130_RSENSE_DEFAULT
#define X_STEP_PIN GPIO_NUM_12 #define X_STEP_PIN GPIO_NUM_12
#define X_DIRECTION_PIN GPIO_NUM_14 #define X_DIRECTION_PIN GPIO_NUM_14
#define X_TRINAMIC // using SPI control
#define X_CS_PIN GPIO_NUM_17 // Daisy Chain, all share same CS pin #define X_CS_PIN GPIO_NUM_17 // Daisy Chain, all share same CS pin
// Y motor connects to the 2nd driver // Y motor connects to the 2nd driver
#define Y_DRIVER_TMC2130 // Which Driver Type? #define Y_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define Y_RSENSE TMC2130_RSENSE_DEFAULT #define Y_RSENSE TMC2130_RSENSE_DEFAULT
#define Y_STEP_PIN GPIO_NUM_27 #define Y_STEP_PIN GPIO_NUM_27
#define Y_DIRECTION_PIN GPIO_NUM_26 #define Y_DIRECTION_PIN GPIO_NUM_26
#define Y_TRINAMIC // using SPI control
#define Y_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin #define Y_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin
// Y2 motor connects to the 2nd driver // Y2 motor connects to the 2nd driver
#define Y2_DRIVER_TMC2130 // Which Driver Type? #define Y2_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define Y2_RSENSE TMC2130_RSENSE_DEFAULT #define Y2_RSENSE TMC2130_RSENSE_DEFAULT
#define Y2_STEP_PIN GPIO_NUM_15 // Z on schem #define Y2_STEP_PIN GPIO_NUM_15 // Z on schem
#define Y2_DIRECTION_PIN GPIO_NUM_2 // Z on schem #define Y2_DIRECTION_PIN GPIO_NUM_2 // Z on schem
#define Y2_TRINAMIC // using SPI control
#define Y2_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin #define Y2_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin
#define Y_AXIS_SQUARING // optional #define Y_AXIS_SQUARING // optional
// Z Axis motor connects to the 4th driver // Z Axis motor connects to the 4th driver
#define Z_DRIVER_TMC2130 // Which Driver Type? #define Z_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define Z_RSENSE TMC2130_RSENSE_DEFAULT #define Z_RSENSE TMC2130_RSENSE_DEFAULT
#define Z_STEP_PIN GPIO_NUM_33 // A on schem #define Z_STEP_PIN GPIO_NUM_33 // A on schem
#define Z_DIRECTION_PIN GPIO_NUM_32 // A on schem #define Z_DIRECTION_PIN GPIO_NUM_32 // A on schem
#define Z_TRINAMIC // using SPI control
#define Z_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin #define Z_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin
// Mist is a 3.3V output // Mist is a 3.3V output
@@ -95,5 +93,3 @@
#define Y_LIMIT_PIN GPIO_NUM_39 #define Y_LIMIT_PIN GPIO_NUM_39
#define Z_LIMIT_PIN GPIO_NUM_34 #define Z_LIMIT_PIN GPIO_NUM_34
#define LIMIT_MASK B0111

View File

@@ -1,5 +1,5 @@
/* /*
spi_daisy_4axis.h spi_daisy_4axis_xyz.h
Part of Grbl_ESP32 Part of Grbl_ESP32
Pin assignments for a 4-axis machine using Triaminic drivers Pin assignments for a 4-axis machine using Triaminic drivers
@@ -23,39 +23,38 @@
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>. along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define MACHINE_NAME "SPI Daisy 4x XYZ" #define MACHINE_NAME "SPI_DAISY_4X_XYZ"
#ifdef N_AXIS #ifdef N_AXIS
#undef N_AXIS #undef N_AXIS
#endif #endif
#define N_AXIS 3 #define N_AXIS 3
#define USE_TRINAMIC
#define TRINAMIC_DAISY_CHAIN #define TRINAMIC_DAISY_CHAIN
#define TRINAMIC_RUN_MODE TRINAMIC_MODE_COOLSTEP
#define TRINAMIC_HOMING_MODE TRINAMIC_MODE_COOLSTEP
// Use SPI enable instead of the enable pin // Use SPI enable instead of the enable pin
// The hardware enable pin is tied to ground // The hardware enable pin is tied to ground
#define USE_TRINAMIC_ENABLE #define USE_TRINAMIC_ENABLE
#define X_DRIVER_TMC2130 // Which Driver Type? #define X_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define X_RSENSE TMC2130_RSENSE_DEFAULT #define X_RSENSE TMC2130_RSENSE_DEFAULT
#define X_STEP_PIN GPIO_NUM_12 #define X_STEP_PIN GPIO_NUM_12
#define X_DIRECTION_PIN GPIO_NUM_14 #define X_DIRECTION_PIN GPIO_NUM_14
#define X_TRINAMIC // using SPI control
#define X_CS_PIN GPIO_NUM_17 // Daisy Chain, all share same CS pin #define X_CS_PIN GPIO_NUM_17 // Daisy Chain, all share same CS pin
#define Y_DRIVER_TMC2130 // Which Driver Type? #define Y_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define Y_RSENSE TMC2130_RSENSE_DEFAULT #define Y_RSENSE TMC2130_RSENSE_DEFAULT
#define Y_STEP_PIN GPIO_NUM_27 #define Y_STEP_PIN GPIO_NUM_27
#define Y_DIRECTION_PIN GPIO_NUM_26 #define Y_DIRECTION_PIN GPIO_NUM_26
#define Y_TRINAMIC // using SPI control
#define Y_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin #define Y_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin
#define Z_DRIVER_TMC2130 // Which Driver Type? #define Z_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define Z_RSENSE TMC2130_RSENSE_DEFAULT #define Z_RSENSE TMC2130_RSENSE_DEFAULT
#define Z_STEP_PIN GPIO_NUM_15 #define Z_STEP_PIN GPIO_NUM_15
#define Z_DIRECTION_PIN GPIO_NUM_2 #define Z_DIRECTION_PIN GPIO_NUM_2
#define Z_TRINAMIC // using SPI control
#define Z_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin #define Z_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin
@@ -79,5 +78,4 @@
#define X_LIMIT_PIN GPIO_NUM_36 #define X_LIMIT_PIN GPIO_NUM_36
#define Y_LIMIT_PIN GPIO_NUM_39 #define Y_LIMIT_PIN GPIO_NUM_39
#define Z_LIMIT_PIN GPIO_NUM_34 #define Z_LIMIT_PIN GPIO_NUM_34
#define LIMIT_MASK B0111

View File

@@ -1,5 +1,5 @@
/* /*
spi_daisy_4axis.h spi_daisy_4axis_xyza.h
Part of Grbl_ESP32 Part of Grbl_ESP32
Pin assignments for a 4-axis machine using Triaminic drivers Pin assignments for a 4-axis machine using Triaminic drivers
@@ -23,54 +23,51 @@
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>. along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define MACHINE_NAME "SPI Daisy 4x XYZA" #define MACHINE_NAME "SPI_DAISY_4X XYZA"
#ifdef N_AXIS #ifdef N_AXIS
#undef N_AXIS #undef N_AXIS
#endif #endif
#define N_AXIS 4 // can be 3 or 4. (if 3 install bypass jumper next to the A driver) #define N_AXIS 4 // can be 3 or 4. (if 3 install bypass jumper next to the A driver)
#define USE_TRINAMIC
#define TRINAMIC_DAISY_CHAIN #define TRINAMIC_DAISY_CHAIN
#define TRINAMIC_RUN_MODE TRINAMIC_MODE_STEALTHCHOP
// Use SPI enable instead of the enable pin // Use SPI enable instead of the enable pin
// The hardware enable pin is tied to ground // The hardware enable pin is tied to ground
#define USE_TRINAMIC_ENABLE #define USE_TRINAMIC_ENABLE
#define X_DRIVER_TMC2130 // Which Driver Type? #define X_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define X_RSENSE TMC2130_RSENSE_DEFAULT #define X_RSENSE TMC2130_RSENSE_DEFAULT
#define X_STEP_PIN GPIO_NUM_12 #define X_STEP_PIN GPIO_NUM_12
#define X_DIRECTION_PIN GPIO_NUM_14 #define X_DIRECTION_PIN GPIO_NUM_14
#define X_TRINAMIC // using SPI control
#define X_CS_PIN GPIO_NUM_17 // Daisy Chain, all share same CS pin #define X_CS_PIN GPIO_NUM_17 // Daisy Chain, all share same CS pin
#define Y_DRIVER_TMC2130 // Which Driver Type? #define Y_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define Y_RSENSE TMC2130_RSENSE_DEFAULT #define Y_RSENSE TMC2130_RSENSE_DEFAULT
#define Y_STEP_PIN GPIO_NUM_27 #define Y_STEP_PIN GPIO_NUM_27
#define Y_DIRECTION_PIN GPIO_NUM_26 #define Y_DIRECTION_PIN GPIO_NUM_26
#define Y_TRINAMIC // using SPI control
#define Y_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin #define Y_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin
#define Z_DRIVER_TMC2130 // Which Driver Type? #define Z_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define Z_RSENSE TMC2130_RSENSE_DEFAULT #define Z_RSENSE TMC2130_RSENSE_DEFAULT
#define Z_STEP_PIN GPIO_NUM_15 #define Z_STEP_PIN GPIO_NUM_15
#define Z_DIRECTION_PIN GPIO_NUM_2 #define Z_DIRECTION_PIN GPIO_NUM_2
#define Z_TRINAMIC // using SPI control
#define Z_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin #define Z_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin
#define A_DRIVER_TMC2130 // Which Driver Type? #define A_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define A_RSENSE TMC2130_RSENSE_DEFAULT #define A_RSENSE TMC2130_RSENSE_DEFAULT
#define A_STEP_PIN GPIO_NUM_33 #define A_STEP_PIN GPIO_NUM_33
#define A_DIRECTION_PIN GPIO_NUM_32 #define A_DIRECTION_PIN GPIO_NUM_32
#define A_TRINAMIC // using SPI control #define A_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin
#define A_CS_PIN X_CS_PIN // Daisy Chain, all share same CS pin
// Mist is a 3.3V output // Mist is a 3.3V output
// Turn on with M7 and off with M9 // Turn on with M7 and off with M9
#define COOLANT_MIST_PIN GPIO_NUM_21 #define COOLANT_MIST_PIN GPIO_NUM_21
#define SPINDLE_OUTPUT_PIN GPIO_NUM_25 #define SPINDLE_OUTPUT_PIN GPIO_NUM_25
#define SPINDLE_ENABLE_PIN GPIO_NUM_4 #define SPINDLE_ENABLE_PIN GPIO_NUM_4
// Relay operation // Relay operation
@@ -90,5 +87,4 @@
#define X_LIMIT_PIN GPIO_NUM_36 #define X_LIMIT_PIN GPIO_NUM_36
#define Y_LIMIT_PIN GPIO_NUM_39 #define Y_LIMIT_PIN GPIO_NUM_39
#define Z_LIMIT_PIN GPIO_NUM_34 #define Z_LIMIT_PIN GPIO_NUM_34
#define A_LIMIT_PIN GPIO_NUM_35 #define A_LIMIT_PIN GPIO_NUM_35
#define LIMIT_MASK B1111

View File

@@ -32,7 +32,6 @@
#define Z_STEP_PIN GPIO_NUM_27 #define Z_STEP_PIN GPIO_NUM_27
#define Z_DIRECTION_PIN GPIO_NUM_33 #define Z_DIRECTION_PIN GPIO_NUM_33
#define LIMIT_MASK B111
#define X_LIMIT_PIN GPIO_NUM_17 #define X_LIMIT_PIN GPIO_NUM_17
#define Y_LIMIT_PIN GPIO_NUM_4 #define Y_LIMIT_PIN GPIO_NUM_4
#define Z_LIMIT_PIN GPIO_NUM_16 #define Z_LIMIT_PIN GPIO_NUM_16
@@ -48,4 +47,4 @@
/* /*
#define SPINDLE_TYPE SPINDLE_TYPE_BESC #define SPINDLE_TYPE SPINDLE_TYPE_BESC
#define SPINDLE_OUTPUT_PIN GPIO_NUM_27 // #define SPINDLE_OUTPUT_PIN GPIO_NUM_27 //
*/ */

View File

@@ -84,11 +84,6 @@
// #define Z_STEP_PIN GPIO_NUM_27 // #define Z_STEP_PIN GPIO_NUM_27
// #define Z_DIRECTION_PIN GPIO_NUM_33 // #define Z_DIRECTION_PIN GPIO_NUM_33
// The 1 bits in LIMIT_MASK set the axes that have limit switches
// For example, if the Y axis has no limit switches but the
// X, Z, A and B axes do, the LIMIT_MASK value would be B11101
// #define LIMIT_MASK B111
// #define X_LIMIT_PIN GPIO_NUM_17 // #define X_LIMIT_PIN GPIO_NUM_17
// #define Y_LIMIT_PIN GPIO_NUM_4 // #define Y_LIMIT_PIN GPIO_NUM_4
// #define Z_LIMIT_PIN GPIO_NUM_16 // #define Z_LIMIT_PIN GPIO_NUM_16
@@ -151,10 +146,10 @@
// not at all. // not at all.
// #undef HOMING_CYCLE_0 // #undef HOMING_CYCLE_0
// #define HOMING_CYCLE_0 ((1<<X_AXIS)|(1<<Y_AXIS)) // #define HOMING_CYCLE_0 (bit(X_AXIS)|bit(Y_AXIS))
// #undef HOMING_CYCLE_1 // #undef HOMING_CYCLE_1
// #define HOMING_CYCLE_1 ((1<<A_AXIS)|(1<<B_AXIS)) // #define HOMING_CYCLE_1 (bit(A_AXIS)|bit(B_AXIS))
// #undef HOMING_CYCLE_2 // #undef HOMING_CYCLE_2
// #endif // #endif

View File

@@ -34,8 +34,6 @@
#define MACHINE_NAME "Test Drive - Demo Only No I/O!" #define MACHINE_NAME "Test Drive - Demo Only No I/O!"
#define LIMIT_MASK 0 // no limit pins
#define SPINDLE_TYPE SPINDLE_TYPE_NONE #define SPINDLE_TYPE SPINDLE_TYPE_NONE
#ifdef USE_RMT_STEPS #ifdef USE_RMT_STEPS

View File

@@ -1,5 +1,5 @@
/* /*
tmc21340_pen.h tmc2130_pen.h
Part of Grbl_ESP32 Part of Grbl_ESP32
Pin assignments for the TMC2130 Pen/Laser controller Pin assignments for the TMC2130 Pen/Laser controller
@@ -35,19 +35,18 @@
#define X_LIMIT_PIN GPIO_NUM_32 #define X_LIMIT_PIN GPIO_NUM_32
#endif #endif
#define USE_TRINAMIC // Using at least 1 trinamic driver #define TRINAMIC_RUN_MODE TRINAMIC_MODE_COOLSTEP
#define TRINAMIC_HOMING_MODE TRINAMIC_MODE_COOLSTEP
#define X_STEP_PIN GPIO_NUM_12 #define X_STEP_PIN GPIO_NUM_12
#define X_DIRECTION_PIN GPIO_NUM_26 #define X_DIRECTION_PIN GPIO_NUM_26
#define X_TRINAMIC // using SPI control #define X_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define X_DRIVER_TMC2130 // Which Driver Type?
#define X_CS_PIN GPIO_NUM_17 //chip select #define X_CS_PIN GPIO_NUM_17 //chip select
#define X_RSENSE TMC2130_RSENSE_DEFAULT #define X_RSENSE TMC2130_RSENSE_DEFAULT
#define Y_STEP_PIN GPIO_NUM_14 #define Y_STEP_PIN GPIO_NUM_14
#define Y_DIRECTION_PIN GPIO_NUM_25 #define Y_DIRECTION_PIN GPIO_NUM_25
#define Y_TRINAMIC // using SPI control #define Y_TRINAMIC_DRIVER 2130 // Which Driver Type?
#define Y_DRIVER_TMC2130 // Which Driver Type?
#define Y_CS_PIN GPIO_NUM_16 //chip select #define Y_CS_PIN GPIO_NUM_16 //chip select
#define Y_RSENSE TMC2130_RSENSE_DEFAULT #define Y_RSENSE TMC2130_RSENSE_DEFAULT
@@ -63,12 +62,9 @@
#ifdef USE_SERVO_AXES #ifdef USE_SERVO_AXES
#define SPINDLE_TYPE SPINDLE_TYPE_NONE #define SPINDLE_TYPE SPINDLE_TYPE_NONE
#define SERVO_Z_PIN GPIO_NUM_27 // comment this out if PWM spindle/laser control. #define Z_SERVO_PIN GPIO_NUM_27 // comment this out if PWM spindle/laser control.
#define SERVO_Z_RANGE_MIN 0.0 #define Z_SERVO_RANGE_MIN 0.0
#define SERVO_Z_RANGE_MAX 5.0 #define Z_SERVO_RANGE_MAX 5.0
#define SERVO_Z_HOMING_TYPE SERVO_HOMING_TARGET // during homing it will instantly move to a target value
#define SERVO_Z_HOME_POS SERVO_Z_RANGE_MAX // move to max during homing
#define SERVO_Z_MPOS false // will not use mpos, uses work coordinates
#else #else
#define SPINDLE_TYPE SPINDLE_TYPE_PWM #define SPINDLE_TYPE SPINDLE_TYPE_PWM
@@ -77,7 +73,6 @@
// #define X_LIMIT_PIN See version section at beginning of file // #define X_LIMIT_PIN See version section at beginning of file
#define Y_LIMIT_PIN GPIO_NUM_4 #define Y_LIMIT_PIN GPIO_NUM_4
#define LIMIT_MASK B11
// defaults // defaults
#define DEFAULT_Z_STEPS_PER_MM 100.0 // This is used as the servo calibration #define DEFAULT_Z_STEPS_PER_MM 100.0 // This is used as the servo calibration

View File

@@ -0,0 +1,477 @@
/*
MotorClass.cpp
Part of Grbl_ESP32
2020 - Bart Dring
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
TODO
Make sure public/private/protected is cleaned up.
Only a few Unipolar axes have been setup in init()
Get rid of Z_SERVO, just reply on Z_SERVO_PIN
Deal with custom machine ... machine_trinamic_setup();
Class is ready to deal with non SPI pins, but they have not been needed yet.
It would be nice in the config message though
Testing
Done (success)
3 Axis (3 Standard Steppers)
MPCNC (ganged with shared direction pin)
TMC2130 Pen Laser (trinamics, stallguard tuning)
Unipolar
TODO
4 Axis SPI (Daisy Chain, Ganged with unique direction pins)
Reference
TMC2130 Datasheet https://www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC2130_datasheet.pdf
*/
#include "../grbl.h"
#include "TrinamicDriverClass.cpp"
#include "StandardStepperClass.cpp"
#include "UnipolarMotorClass.cpp"
#include "RcServoClass.cpp"
//#include "SolenoidClass.cpp"
Motor* myMotor[MAX_AXES][MAX_GANGED]; // number of axes (normal and ganged)
static TaskHandle_t readSgTaskHandle = 0; // for realtime stallguard data diaplay
static TaskHandle_t servoUpdateTaskHandle = 0;
uint8_t rmt_chan_num[MAX_AXES][MAX_GANGED];
rmt_item32_t rmtItem[2];
rmt_config_t rmtConfig;
bool motor_class_steps; // true if at least one motor class is handling steps
void init_motors() {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Init Motors");
#ifdef X_TRINAMIC_DRIVER
myMotor[X_AXIS][0] = new TrinamicDriver(X_AXIS, X_STEP_PIN, X_DIRECTION_PIN, X_DISABLE_PIN, X_CS_PIN, X_TRINAMIC_DRIVER, X_RSENSE, get_next_trinamic_driver_index());
#elif defined(X_SERVO_PIN)
myMotor[X_AXIS][0] = new RcServo(X_AXIS, X_SERVO_PIN, X_SERVO_RANGE_MIN, X_SERVO_RANGE_MAX);
#elif defined(X_UNIPOLAR)
myMotor[X_AXIS][0] = new UnipolarMotor(X_AXIS, X_PIN_PHASE_0, X_PIN_PHASE_1, X_PIN_PHASE_2, X_PIN_PHASE_3);
#elif defined(X_STEP_PIN)
myMotor[X_AXIS][0] = new StandardStepper(X_AXIS, X_STEP_PIN, X_DIRECTION_PIN, X_DISABLE_PIN);
#else
myMotor[X_AXIS][0] = new Nullmotor();
#endif
#ifdef X2_TRINAMIC_DRIVER
myMotor[X_AXIS][1] = new TrinamicDriver(X2_AXIS, X2_STEP_PIN, X2_DIRECTION_PIN, X2_DISABLE_PIN, X2_CS_PIN, X2_TRINAMIC_DRIVER, X2_RSENSE, get_next_trinamic_driver_index());
#elif defined(X2_SERVO_PIN)
myMotor[X_AXIS][1] = new RcServo(X2_AXIS, X2_SERVO_PIN, X2_SERVO_RANGE_MIN, X2_SERVO_RANGE_MAX);
#elif defined(X2_UNIPOLAR)
myMotor[X_AXIS][1] = new UnipolarMotor(X2_AXIS, X2_PIN_PHASE_0, X2_PIN_PHASE_1, X2_PIN_PHASE_2, X2_PIN_PHASE_3);
#elif defined(X2_STEP_PIN)
myMotor[X_AXIS][1] = new StandardStepper(X2_AXIS, X2_STEP_PIN, X2_DIRECTION_PIN, X2_DISABLE_PIN);
#else
myMotor[X_AXIS][1] = new Nullmotor();
#endif
// this WILL be done better with settings
#ifdef Y_TRINAMIC_DRIVER
myMotor[Y_AXIS][0] = new TrinamicDriver(Y_AXIS, Y_STEP_PIN, Y_DIRECTION_PIN, Y_DISABLE_PIN, Y_CS_PIN, Y_TRINAMIC_DRIVER, Y_RSENSE, get_next_trinamic_driver_index());
#elif defined(Y_SERVO_PIN)
myMotor[Y_AXIS][0] = new RcServo(Y_AXIS, Y_SERVO_PIN, Y_SERVO_RANGE_MIN, Y_SERVO_RANGE_MAX);
#elif defined(Y_UNIPOLAR)
myMotor[Y_AXIS][0] = new UnipolarMotor(Y_AXIS, Y_PIN_PHASE_0, Y_PIN_PHASE_1, Y_PIN_PHASE_2, Y_PIN_PHASE_3);
#elif defined(Y_STEP_PIN)
myMotor[Y_AXIS][0] = new StandardStepper(Y_AXIS, Y_STEP_PIN, Y_DIRECTION_PIN, Y_DISABLE_PIN);
#else
myMotor[Y_AXIS][0] = new Nullmotor();
#endif
#ifdef Y2_TRINAMIC_DRIVER
myMotor[Y_AXIS][1] = new TrinamicDriver(Y2_AXIS, Y2_STEP_PIN, Y2_DIRECTION_PIN, Y2_DISABLE_PIN, Y2_CS_PIN, Y2_TRINAMIC_DRIVER, Y2_RSENSE, get_next_trinamic_driver_index());
#elif defined(Y2_SERVO_PIN)
myMotor[Y_AXIS][1] = new RcServo(Y2_AXIS, Y2_SERVO_PIN, Y2_SERVO_RANGE_MIN, Y2_SERVO_RANGE_MAX);
#elif defined(Y2_UNIPOLAR)
myMotor[Y_AXIS][1] = new UnipolarMotor(Y2_AXIS, Y2_PIN_PHASE_0, Y2_PIN_PHASE_1, Y2_PIN_PHASE_2, Y2_PIN_PHASE_3);
#elif defined(Y2_STEP_PIN)
myMotor[Y_AXIS][1] = new StandardStepper(Y2_AXIS, Y2_STEP_PIN, Y2_DIRECTION_PIN, Y2_DISABLE_PIN);
#else
myMotor[Y_AXIS][1] = new Nullmotor();
#endif
// this WILL be done better with settings
#ifdef Z_TRINAMIC_DRIVER
myMotor[Z_AXIS][0] = new TrinamicDriver(Z_AXIS, Z_STEP_PIN, Z_DIRECTION_PIN, Z_DISABLE_PIN, Z_CS_PIN, Z_TRINAMIC_DRIVER, Z_RSENSE, get_next_trinamic_driver_index());
#elif defined(Z_SERVO_PIN)
myMotor[Z_AXIS][0] = new RcServo(Z_AXIS, Z_SERVO_PIN, Z_SERVO_RANGE_MIN, Z_SERVO_RANGE_MAX);
#elif defined(Z_UNIPOLAR)
myMotor[Z_AXIS][0] = new UnipolarMotor(Z_AXIS, Z_PIN_PHASE_0, Z_PIN_PHASE_1, Z_PIN_PHASE_2, Z_PIN_PHASE_3);
#elif defined(Z_STEP_PIN)
myMotor[Z_AXIS][0] = new StandardStepper(Z_AXIS, Z_STEP_PIN, Z_DIRECTION_PIN, Z_DISABLE_PIN);
#else
myMotor[Z_AXIS][0] = new Nullmotor();
#endif
#ifdef Z2_TRINAMIC_DRIVER
myMotor[Z_AXIS][1] = new TrinamicDriver(Z2_AXIS, Z2_STEP_PIN, Z2_DIRECTION_PIN, Z2_DISABLE_PIN, Z2_CS_PIN, Z2_TRINAMIC_DRIVER, Z2_RSENSE, get_next_trinamic_driver_index());
#elif defined(Z2_SERVO_PIN)
myMotor[Z_AXIS][1] = new RcServo(Z2_AXIS, Z2_SERVO_PIN, Z2_SERVO_RANGE_MIN, Z2_SERVO_RANGE_MAX);
#elif defined(Z2_UNIPOLAR)
myMotor[Z_AXIS][1] = new UnipolarMotor(Z2_AXIS, Z2_PIN_PHASE_0, Z2_PIN_PHASE_1, Z2_PIN_PHASE_2, Z2_PIN_PHASE_3);
#elif defined(Z2_STEP_PIN)
myMotor[Z_AXIS][1] = new StandardStepper(Z2_AXIS, Z2_STEP_PIN, Z2_DIRECTION_PIN, Z2_DISABLE_PIN);
#else
myMotor[Z_AXIS][1] = new Nullmotor();
#endif
// this WILL be done better with settings
#ifdef A_TRINAMIC_DRIVER
myMotor[A_AXIS][0] = new TrinamicDriver(A_AXIS, A_STEP_PIN, A_DIRECTION_PIN, A_DISABLE_PIN, A_CS_PIN, A_TRINAMIC_DRIVER, A_RSENSE, get_next_trinamic_driver_index());
#elif defined(A_SERVO_PIN)
myMotor[A_AXIS][0] = new RcServo(A_AXIS, A_SERVO_PIN, A_SERVO_RANGE_MIN, A_SERVO_RANGE_MAX);
#elif defined(A_UNIPOLAR)
myMotor[A_AXIS][0] = new UnipolarMotor(A_AXIS, A_PIN_PHASE_0, A_PIN_PHASE_1, A_PIN_PHASE_2, A_PIN_PHASE_3);
#elif defined(A_STEP_PIN)
myMotor[A_AXIS][0] = new StandardStepper(A_AXIS, A_STEP_PIN, A_DIRECTION_PIN, A_DISABLE_PIN);
#else
myMotor[A_AXIS][0] = new Nullmotor();
#endif
#ifdef A2_TRINAMIC_DRIVER
myMotor[A_AXIS][1] = new TrinamicDriver(A2_AXIS, A2_STEP_PIN, A2_DIRECTION_PIN, A2_DISABLE_PIN, A2_CS_PIN, A2_TRINAMIC_DRIVER, A2_RSENSE, get_next_trinamic_driver_index());
#elif defined(A2_SERVO_PIN)
myMotor[A_AXIS][1] = new RcServo(A2_AXIS, A2_SERVO_PIN, A2_SERVO_RANGE_MIN, A2_SERVO_RANGE_MAX);
#elif defined(A2_UNIPOLAR)
myMotor[A_AXIS][1] = new UnipolarMotor(A2_AXIS, A2_PIN_PHASE_0, A2_PIN_PHASE_1, A2_PIN_PHASE_2, A2_PIN_PHASE_3);
#elif defined(A2_STEP_PIN)
myMotor[A_AXIS][1] = new StandardStepper(A2_AXIS, A2_STEP_PIN, A2_DIRECTION_PIN, A2_DISABLE_PIN);
#else
myMotor[A_AXIS][1] = new Nullmotor();
#endif
// this WILL be done better with settings
#ifdef B_TRINAMIC_DRIVER
myMotor[B_AXIS][0] = new TrinamicDriver(B_AXIS, B_STEP_PIN, B_DIRECTION_PIN, B_DISABLE_PIN, B_CS_PIN, B_TRINAMIC_DRIVER, B_RSENSE, get_next_trinamic_driver_index());
#elif defined(B_SERVO_PIN)
myMotor[B_AXIS][0] = new RcServo(B_AXIS, B_SERVO_PIN, B_SERVO_RANGE_MIN, B_SERVO_RANGE_MAX);
#elif defined(B_UNIPOLAR)
myMotor[B_AXIS][0] = new UnipolarMotor(B_AXIS, B_PIN_PHASE_0, B_PIN_PHASE_1, B_PIN_PHASE_2, B_PIN_PHASE_3);
#elif defined(B_STEP_PIN)
myMotor[B_AXIS][0] = new StandardStepper(B_AXIS, B_STEP_PIN, B_DIRECTION_PIN, B_DISABLE_PIN);
#else
myMotor[B_AXIS][0] = new Nullmotor();
#endif
#ifdef B2_TRINAMIC_DRIVER
myMotor[B_AXIS][1] = new TrinamicDriver(B2_AXIS, B2_STEP_PIN, B2_DIRECTION_PIN, B2_DISABLE_PIN, B2_CS_PIN, B2_TRINAMIC_DRIVER, B2_RSENSE, get_next_trinamic_driver_index());
#elif defined(B2_SERVO_PIN)
myMotor[B_AXIS][1] = new RcServo(B2_AXIS, B2_SERVO_PIN, B2_SERVO_RANGE_MIN, B2_SERVO_RANGE_MAX);
#elif defined(B2_UNIPOLAR)
myMotor[B_AXIS][1] = new UnipolarMotor(B2_AXIS, B2_PIN_PHASE_0, B2_PIN_PHASE_1, B2_PIN_PHASE_2, B2_PIN_PHASE_3);
#elif defined(B2_STEP_PIN)
myMotor[B_AXIS][1] = new StandardStepper(B2_AXIS, B2_STEP_PIN, B2_DIRECTION_PIN, B2_DISABLE_PIN);
#else
myMotor[B_AXIS][1] = new Nullmotor();
#endif
// this WILL be done better with settings
#ifdef C_TRINAMIC_DRIVER
myMotor[C_AXIS][0] = new TrinamicDriver(C_AXIS, C_STEP_PIN, C_DIRECTION_PIN, C_DISABLE_PIN, C_CS_PIN, C_TRINAMIC_DRIVER, C_RSENSE, get_next_trinamic_driver_index());
#elif defined(C_SERVO_PIN)
myMotor[C_AXIS][0] = new RcServo(C_AXIS, C_SERVO_PIN, C_SERVO_RANGE_MIN, C_SERVO_RANGE_MAX);
#elif defined(C_UNIPOLAR)
myMotor[C_AXIS][0] = new UnipolarMotor(C_AXIS, C_PIN_PHASE_0, C_PIN_PHASE_1, C_PIN_PHASE_2, C_PIN_PHASE_3);
#elif defined(C_STEP_PIN)
myMotor[C_AXIS][0] = new StandardStepper(C_AXIS, C_STEP_PIN, C_DIRECTION_PIN, C_DISABLE_PIN);
#else
myMotor[C_AXIS][0] = new Nullmotor();
#endif
#ifdef C2_TRINAMIC_DRIVER
myMotor[C_AXIS][1] = new TrinamicDriver(C2_AXIS, C2_STEP_PIN, C2_DIRECTION_PIN, C2_DISABLE_PIN, C2_CS_PIN, C2_TRINAMIC_DRIVER, C2_RSENSE, get_next_trinamic_driver_index());
#elif defined(C2_SERVO_PIN)
myMotor[C_AXIS][1] = new RcServo(C2_AXIS, C2_SERVO_PIN, C2_SERVO_RANGE_MIN, C2_SERVO_RANGE_MAX);
#elif defined(C2_UNIPOLAR)
myMotor[C_AXIS][1] = new UnipolarMotor(C2_AXIS, C2_PIN_PHASE_0, C2_PIN_PHASE_1, C2_PIN_PHASE_2, C2_PIN_PHASE_3);
#elif defined(C2_STEP_PIN)
myMotor[C_AXIS][1] = new StandardStepper(C2_AXIS, C2_STEP_PIN, C2_DIRECTION_PIN, C2_DISABLE_PIN);
#else
myMotor[C_AXIS][1] = new Nullmotor();
#endif
#ifdef USE_STEPSTICK
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Using StepStick Mode");
#ifdef STEPPER_MS1
digitalWrite(STEPPER_MS1, HIGH);
pinMode(STEPPER_MS1, OUTPUT);
#endif
#ifdef STEPPER_MS2
digitalWrite(STEPPER_MS2, HIGH);
pinMode(STEPPER_MS2, OUTPUT);
#endif
#ifdef STEPPER_X_MS3
digitalWrite(STEPPER_X_MS3, HIGH);
pinMode(STEPPER_X_MS3, OUTPUT);
#endif
#ifdef STEPPER_Y_MS3
digitalWrite(STEPPER_Y_MS3, HIGH);
pinMode(STEPPER_Y_MS3, OUTPUT);
#endif
#ifdef STEPPER_Z_MS3
digitalWrite(STEPPER_Z_MS3, HIGH);
pinMode(STEPPER_Z_MS3, OUTPUT);
#endif
#ifdef STEPPER_A_MS3
digitalWrite(STEPPER_A_MS3, HIGH);
pinMode(STEPPER_A_MS3, OUTPUT);
#endif
#ifdef STEPPER_B_MS3
digitalWrite(STEPPER_B_MS3, HIGH);
pinMode(STEPPER_B_MS3, OUTPUT);
#endif
#ifdef STEPPER_C_MS3
digitalWrite(STEPPER_C_MS3, HIGH);
pinMode(STEPPER_C_MS3, OUTPUT);
#endif
#ifdef STEPPER_RESET
// !RESET pin on steppers (MISO On Schematic)
digitalWrite(STEPPER_RESET, HIGH);
pinMode(STEPPER_RESET, OUTPUT);
#endif
#endif
if (STEPPERS_DISABLE_PIN != UNDEFINED_PIN) {
pinMode(STEPPERS_DISABLE_PIN, OUTPUT); // global motor enable pin
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"Global stepper disable pin:%s",
pinName(STEPPERS_DISABLE_PIN));
}
// certain motors need features to be turned on. Check them here
for (uint8_t axis = X_AXIS; axis < N_AXIS; axis++) {
for (uint8_t gang_index = 0; gang_index < 2; gang_index++) {
if (myMotor[axis][gang_index]->type_id == UNIPOLAR_MOTOR)
motor_class_steps = true;
// CS Pins of all TMC motors need to be setup before any can be talked to
// ...so init cannot be called via the constructors. This inits them all.
if (myMotor[axis][gang_index]->type_id == TRINAMIC_SPI_MOTOR)
myMotor[axis][gang_index]->init();
}
}
// some motor objects require a step signal
motor_class_steps = motors_have_type_id(UNIPOLAR_MOTOR);
if (motors_have_type_id(TRINAMIC_SPI_MOTOR)) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TMCStepper Library Ver. 0x%06x", TMCSTEPPER_VERSION);
xTaskCreatePinnedToCore(readSgTask, // task
"readSgTask", // name for task
4096, // size of task stack
NULL, // parameters
1, // priority
&readSgTaskHandle,
0 // core
);
if (stallguard_debug_mask->get() != 0)
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Stallguard debug enabled: %d", stallguard_debug_mask->get());
}
if (motors_have_type_id(RC_SERVO_MOTOR)) {
xTaskCreatePinnedToCore(servoUpdateTask, // task
"servoUpdateTask", // name for task
4096, // size of task stack
NULL, // parameters
1, // priority
&servoUpdateTaskHandle,
0 // core
);
}
}
void servoUpdateTask(void* pvParameters) {
TickType_t xLastWakeTime;
const TickType_t xUpdate = SERVO_TIMER_INT_FREQ; // in ticks (typically ms)
xLastWakeTime = xTaskGetTickCount(); // Initialise the xLastWakeTime variable with the current time.
while (true) { // don't ever return from this or the task dies
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo update");
for (uint8_t axis = X_AXIS; axis < N_AXIS; axis++) {
for (uint8_t gang_index = 0; gang_index < 2; gang_index++)
myMotor[axis][gang_index]->update();
}
vTaskDelayUntil(&xLastWakeTime, xUpdate);
}
}
// do any motors match the type_id
bool motors_have_type_id(motor_class_id_t id) {
for (uint8_t axis = X_AXIS; axis < N_AXIS; axis++) {
for (uint8_t gang_index = 0; gang_index < 2; gang_index++) {
if (myMotor[axis][gang_index]->type_id == id)
return true;
}
}
return false;
}
void motors_set_disable(bool disable) {
static bool previous_state = false;
if (previous_state == disable)
return;
previous_state = disable;
if (step_enable_invert->get()) {
disable = !disable; // Apply pin invert.
}
digitalWrite(STEPPERS_DISABLE_PIN, disable);
// now loop through all the motors to see if they can individually diable
for (uint8_t gang_index = 0; gang_index < MAX_GANGED; gang_index++) {
for (uint8_t axis = X_AXIS; axis < N_AXIS; axis++)
myMotor[axis][gang_index]->set_disable(disable);
}
}
void motors_read_settings() {
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Read Settings");
for (uint8_t gang_index = 0; gang_index < 2; gang_index++) {
for (uint8_t axis = X_AXIS; axis < N_AXIS; axis++)
myMotor[axis][gang_index]->read_settings();
}
}
// use this to tell all the motors what the current homing mode is
// They can use this to setup things like Stall
void motors_set_homing_mode(uint8_t homing_mask, bool isHoming) {
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "motors_set_homing_mode(%d)", is_homing);
for (uint8_t gang_index = 0; gang_index < 2; gang_index++) {
for (uint8_t axis = X_AXIS; axis < N_AXIS; axis++)
if (bit(axis) & homing_mask)
myMotor[axis][gang_index]->set_homing_mode(homing_mask, isHoming);
}
}
void motors_set_direction_pins(uint8_t onMask) {
static uint8_t previous_val = 255; // should never be this value
if (previous_val == onMask)
return;
previous_val = onMask;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "motors_set_direction_pins:0x%02X", onMask);
for (uint8_t gang_index = 0; gang_index < MAX_GANGED; gang_index++) {
for (uint8_t axis = X_AXIS; axis < N_AXIS; axis++)
myMotor[axis][gang_index]->set_direction_pins(onMask);
}
}
// for testing
#ifndef USE_TRINAMIC
// returns the next spi index. We cannot preassign to axes because ganged (X2 type axes) might
// need to be inserted into the order of axes.
uint8_t get_next_trinamic_driver_index() {
#ifdef TRINAMIC_DAISY_CHAIN
static uint8_t index = 1; // they start at 1
return index++;
#else
return -1;
#endif
}
// some motor objects, like unipolar need step signals
void motors_step(uint8_t step_mask, uint8_t dir_mask) {
if (motor_class_steps) { // determined in init_motors if any motors need to handle steps
for (uint8_t gang_index = 0; gang_index < 2; gang_index++) {
for (uint8_t axis = X_AXIS; axis < N_AXIS; axis++)
myMotor[axis][gang_index]->step(step_mask, dir_mask);
}
}
}
/*
This will print StallGuard data that is useful for tuning.
*/
void readSgTask(void* pvParameters) {
TickType_t xLastWakeTime;
const TickType_t xreadSg = 200; // in ticks (typically ms)
xLastWakeTime = xTaskGetTickCount(); // Initialise the xLastWakeTime variable with the current time.
while (true) { // don't ever return from this or the task dies
if (motorSettingChanged) {
motors_read_settings();
motorSettingChanged = false;
}
if (stallguard_debug_mask->get() != 0) {
if (sys.state == STATE_CYCLE || sys.state == STATE_HOMING || sys.state == STATE_JOG) {
for (uint8_t axis = X_AXIS; axis < N_AXIS; axis++) {
if (stallguard_debug_mask->get() & bit(axis)) {
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "SG:%d", stallguard_debug_mask->get());
for (uint8_t gang_index = 0; gang_index < 2; gang_index++)
myMotor[axis][gang_index]->debug_message();
}
}
} // sys.state
} // if mask
vTaskDelayUntil(&xLastWakeTime, xreadSg);
}
}
#ifdef USE_I2S_OUT
//
// Override default function and insert a short delay
//
void TMC2130Stepper::switchCSpin(bool state) {
digitalWrite(_pinCS, state);
i2s_out_delay();
}
#endif
#endif
// ============================== Class Methods ================================================
Motor :: Motor() {
type_id = MOTOR;
}
void Motor :: init() {
_homing_mask = 0;
}
void Motor :: config_message() {}
void Motor :: debug_message() {}
void Motor :: read_settings() {}
void Motor :: set_disable(bool disable) {}
void Motor :: set_direction_pins(uint8_t onMask) {}
void Motor :: step(uint8_t step_mask, uint8_t dir_mask) {}
bool Motor :: test() {return true;}; // true = OK
void Motor :: update() {}
void Motor :: set_axis_name() {
sprintf(_axis_name, "%c%s", report_get_axis_letter(axis_index), dual_axis_index ? "2" : "");
}
void Motor :: set_homing_mode(uint8_t homing_mask, bool isHoming) {
_homing_mask = homing_mask;
}

View File

@@ -0,0 +1,218 @@
/*
MotorClass.h
Header file for Motor Classes
Here is the hierarchy
Motor
Nullmotor
StandardStepper
TrinamicDriver
Unipolar
RC Servo
These are for motors coordinated by Grbl_ESP32
See motorClass.cpp for more details
Part of Grbl_ESP32
2020 - Bart Dring
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MOTORCLASS_H
#define MOTORCLASS_H
#include "../grbl.h"
#include <TMCStepper.h> // https://github.com/teemuatlut/TMCStepper
#include "TrinamicDriverClass.h"
#include "RcServoClass.h"
//#include "SolenoidClass.h"
extern uint8_t rmt_chan_num[MAX_AXES][2];
extern rmt_item32_t rmtItem[2];
extern rmt_config_t rmtConfig;
typedef enum {
MOTOR,
NULL_MOTOR,
STANDARD_MOTOR,
TRINAMIC_SPI_MOTOR,
UNIPOLAR_MOTOR,
RC_SERVO_MOTOR,
SOLENOID
} motor_class_id_t;
// These are used for setup and to talk to the motors as a group.
void init_motors();
uint8_t get_next_trinamic_driver_index();
bool motors_have_type_id(motor_class_id_t id);
void readSgTask(void* pvParameters);
void motors_read_settings();
void motors_set_homing_mode(uint8_t homing_mask, bool isHoming);
void motors_set_disable(bool disable);
void motors_set_direction_pins(uint8_t onMask);
void motors_step(uint8_t step_mask, uint8_t dir_mask);
void servoUpdateTask(void* pvParameters);
extern bool motor_class_steps; // true if at least one motor class is handling steps
// ==================== Motor Classes ====================
class Motor {
public:
Motor();
virtual void init(); // not in constructor because this also gets called when $$ settings change
virtual void config_message();
virtual void debug_message();
virtual void read_settings();
virtual void set_homing_mode(uint8_t homing_mask, bool isHoming);
virtual void set_disable(bool disable);
virtual void set_direction_pins(uint8_t onMask);
virtual void step(uint8_t step_mask, uint8_t dir_mask); // only used on Unipolar right now
virtual bool test();
virtual void set_axis_name();
virtual void update();
motor_class_id_t type_id;
uint8_t is_active = false;
protected:
uint8_t axis_index; // X_AXIS, etc
uint8_t dual_axis_index; // 0 = primary 1=ganged
bool _showError;
bool _use_mpos = true;
uint8_t _homing_mask;
char _axis_name[10];// this the name to use when reporting like "X" or "X2"
};
class Nullmotor : public Motor {
};
class StandardStepper : public Motor {
public:
StandardStepper();
StandardStepper(uint8_t axis_index, uint8_t step_pin, uint8_t dir_pin, uint8_t disable_pin);
virtual void config_message();
virtual void init();
virtual void set_direction_pins(uint8_t onMask);
void init_step_dir_pins();
virtual void set_disable(bool disable);
uint8_t step_pin;
protected:
bool _invert_step_pin;
uint8_t dir_pin;
uint8_t disable_pin;
};
class TrinamicDriver : public StandardStepper {
public:
TrinamicDriver(uint8_t axis_index,
uint8_t step_pin,
uint8_t dir_pin,
uint8_t disable_pin,
uint8_t cs_pin,
uint16_t driver_part_number,
float r_sense,
int8_t spi_index);
void config_message();
void init();
void set_mode(bool isHoming);
void read_settings();
void trinamic_test_response();
void trinamic_stepper_enable(bool enable);
void debug_message();
void set_homing_mode(uint8_t homing_mask, bool ishoming);
void set_disable(bool disable);
bool test();
private:
uint32_t calc_tstep(float speed, float percent);
TMC2130Stepper* tmcstepper; // all other driver types are subclasses of this one
uint8_t _homing_mode;
uint8_t cs_pin = UNDEFINED_PIN; // The chip select pin (can be the same for daisy chain)
uint16_t _driver_part_number; // example: use 2130 for TMC2130
float _r_sense;
int8_t spi_index;
protected:
uint8_t _mode;
uint8_t _lastMode = 255;
};
class UnipolarMotor : public Motor {
public:
UnipolarMotor();
UnipolarMotor(uint8_t axis_index, uint8_t pin_phase0, uint8_t pin_phase1, uint8_t pin_phase2, uint8_t pin_phase3);
void init();
void config_message();
void set_disable(bool disable);
void step(uint8_t step_mask, uint8_t dir_mask); // only used on Unipolar right now
private:
uint8_t _pin_phase0;
uint8_t _pin_phase1;
uint8_t _pin_phase2;
uint8_t _pin_phase3;
uint8_t _current_phase;
bool _half_step;
bool _enabled;
};
class RcServo : public Motor {
public:
RcServo();
RcServo(uint8_t axis_index, uint8_t pwm_pin, float min, float max);
virtual void config_message();
virtual void init();
void _write_pwm(uint32_t duty);
virtual void set_disable(bool disable);
virtual void update();
void read_settings();
void set_homing_mode(bool is_homing, bool isHoming);
protected:
void set_location();
void _get_calibration();
uint8_t _pwm_pin;
uint8_t _channel_num;
uint32_t _current_pwm_duty;
bool _disabled;
float _position_min;
float _position_max; // position in millimeters
float _homing_position;
float _pwm_pulse_min;
float _pwm_pulse_max;
};
class Solenoid : public RcServo {
public:
Solenoid();
Solenoid(uint8_t axis_index, gpio_num_t pwm_pin, float transition_poiont);
void config_message();
void set_location();
void update();
void init();
void set_disable(bool disable);
float _transition_poiont;
};
#endif

View File

@@ -0,0 +1,190 @@
/*
RcServoServoClass.cpp
This allows an RcServo to be used like any other motor. Serrvos
do have limitation in travel and speed, so you do need to respect that.
Part of Grbl_ESP32
2020 - Bart Dring
Servos have a limited travel, so they map the travel across a range in
the current work coordinatee system. The servo can only travel as far
as the range, but the internal axis value can keep going.
Range: The range is specified in the machine definition file with...
#define X_SERVO_RANGE_MIN 0.0
#define X_SERVO_RANGE_MAX 5.0
Direction: The direction can be changed using the $3 setting for the axis
Homing: During homing, the servo will move to one of the endpoints. The
endpoint is determined by the $23 or $HomingDirInvertMask setting for the axis.
Do not define a homing cycle for the axis with the servo.
You do need at least 1 homing cycle. TODO: Fix this
Calibration. You can tweak the endpoints using the $10n or nStepsPerMm and
$13n or $xMaxTravel setting, where n is the axis.
The value is a percent. If you secify a percent outside the
the range specified by the values below, it will be reset to 100.0 (100% ... no change)
The calibration adjusts in direction of positive momement, so a value above 100% moves
towards the higher axis value.
#define SERVO_CAL_MIN
#define SERVO_CAL_MAX
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
RcServo :: RcServo() {
}
RcServo :: RcServo(uint8_t axis_index, uint8_t pwm_pin, float min, float max) {
type_id = RC_SERVO_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < MAX_AXES ? 0 : 1; // 0 = primary 1 = ganged
this->_pwm_pin = pwm_pin;
_position_min = min;
_position_max = max;
init();
}
void RcServo :: init() {
read_settings();
_channel_num = sys_get_next_PWM_chan_num();
ledcSetup(_channel_num, SERVO_PULSE_FREQ, SERVO_PULSE_RES_BITS);
ledcAttachPin(_pwm_pin, _channel_num);
_current_pwm_duty = 0;
is_active = true; // as opposed to NullMotors, this is a real motor
set_axis_name();
config_message();
}
void RcServo :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis RC Servo motor Output:%d Min:%5.3fmm Max:%5.3fmm",
_axis_name,
_pwm_pin,
_position_min,
_position_max);
}
void RcServo::_write_pwm(uint32_t duty) {
// to prevent excessive calls to ledcWrite, make sure duty hass changed
if (duty == _current_pwm_duty)
return;
_current_pwm_duty = duty;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Servo Pwm %d", _axis_name, duty);
ledcWrite(_channel_num, duty);
}
// sets the PWM to zero. This allows most servos to be manually moved
void RcServo::set_disable(bool disable) {
return;
_disabled = disable;
if (_disabled)
_write_pwm(0);
}
void RcServo::set_homing_mode(bool is_homing, bool isHoming) {
float home_pos = 0.0;
if (!is_homing)
return;
if (bit_istrue(homing_dir_mask->get(), bit(axis_index)))
home_pos = _position_min;
else
home_pos = _position_max;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo set home %d %3.2f", is_homing, home_pos);
sys_position[axis_index] = home_pos * axis_settings[axis_index]->steps_per_mm->get(); // convert to steps
}
void RcServo::update() {
set_location();
}
void RcServo::set_location() {
uint32_t servo_pulse_len;
float servo_pos, mpos, offset;
// skip location if we are in alarm mode
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "locate");
_get_calibration();
if (sys.state == STATE_ALARM) {
set_disable(true);
return;
}
mpos = system_convert_axis_steps_to_mpos(sys_position, axis_index); // get the axis machine position in mm
offset = gc_state.coord_system[axis_index] + gc_state.coord_offset[axis_index]; // get the current axis work offset
servo_pos = mpos - offset; // determine the current work position
// determine the pulse length
servo_pulse_len = (uint32_t)mapConstrain(servo_pos, _position_min, _position_max, _pwm_pulse_min, _pwm_pulse_max);
_write_pwm(servo_pulse_len);
}
void RcServo::read_settings() {
_get_calibration();
}
// this should change to use its own settings.
void RcServo::_get_calibration() {
float _cal_min = 1.0;
float _cal_max = 1.0;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Read settings");
// make sure the min is in range
if ((axis_settings[axis_index]->steps_per_mm->get() < SERVO_CAL_MIN) || (axis_settings[axis_index]->steps_per_mm->get() > SERVO_CAL_MAX)) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration ($10%d) value error. Reset to 100", axis_index);
char reset_val[] = "100";
axis_settings[axis_index]->steps_per_mm->setStringValue(reset_val);
}
// make sure the max is in range
// Note: Max travel is set positive via $$, but stored as a negative number
if ((axis_settings[axis_index]->max_travel->get() < SERVO_CAL_MIN) || (axis_settings[axis_index]->max_travel->get() > SERVO_CAL_MAX)) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration ($13%d) value error. %3.2f Reset to 100", axis_index, axis_settings[axis_index]->max_travel->get());
char reset_val[] = "100";
axis_settings[axis_index]->max_travel->setStringValue(reset_val);
}
_pwm_pulse_min = SERVO_MIN_PULSE;
_pwm_pulse_max = SERVO_MAX_PULSE;
if (bit_istrue(dir_invert_mask->get(), bit(axis_index))) { // normal direction
_cal_min = 2.0 - (axis_settings[axis_index]->steps_per_mm->get() / 100.0);
_cal_max = 2.0 - (axis_settings[axis_index]->max_travel->get() / 100.0);
swap(_pwm_pulse_min, _pwm_pulse_max);
} else { // inverted direction
_cal_min = (axis_settings[axis_index]->steps_per_mm->get() / 100.0);
_cal_max = (axis_settings[axis_index]->max_travel->get() / 100.0);
}
_pwm_pulse_min *= _cal_min;
_pwm_pulse_max *= _cal_max;
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration min:%1.2f max %1.2f", _pwm_pulse_min, _pwm_pulse_max);
}

View File

@@ -0,0 +1,47 @@
/*
RcServoClass.h
Part of Grbl_ESP32
2020 - Bart Dring
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RCSERVOCLASS_H
#define RCSERVOCLASS_H
// this is the pulse range of a the servo. Typical servos are 0.001 to 0.002 seconds
// some servos have a wider range. You can adjust this here or in the calibration feature
#define SERVO_MIN_PULSE_SEC 0.001 // min pulse in seconds
#define SERVO_MAX_PULSE_SEC 0.002 // max pulse in seconds
#define SERVO_POSITION_MIN_DEFAULT 0.0 // mm
#define SERVO_POSITION_MAX_DEFAULT 20.0 // mm
#define SERVO_PULSE_FREQ 50 // 50Hz ...This is a standard analog servo value. Digital ones can repeat faster
#define SERVO_PULSE_RES_BITS 16 // bits of resolution of PWM (16 is max)
#define SERVO_PULSE_RES_COUNT 65535 // see above TODO...do the math here 2^SERVO_PULSE_RES_BITS
#define SERVO_TIME_PER_BIT ((1.0 / (float)SERVO_PULSE_FREQ) / ((float)SERVO_PULSE_RES_COUNT) ) // seconds
#define SERVO_MIN_PULSE (uint16_t)(SERVO_MIN_PULSE_SEC / SERVO_TIME_PER_BIT) // in timer counts
#define SERVO_MAX_PULSE (uint16_t)(SERVO_MAX_PULSE_SEC / SERVO_TIME_PER_BIT) // in timer counts
#define SERVO_PULSE_RANGE (SERVO_MAX_PULSE-SERVO_MIN_PULSE)
#define SERVO_CAL_MIN 20.0 // Percent: the minimum allowable calibration value
#define SERVO_CAL_MAX 180.0 // Percent: the maximum allowable calibration value
#define SERVO_TIMER_INT_FREQ 50.0 // Hz This is the task frequency
#endif

View File

@@ -0,0 +1,107 @@
/*
StandardStepperClass.cpp
This is used for a stepper motor that just requires step and direction
pins.
TODO: Add an enable pin
Part of Grbl_ESP32
2020 - Bart Dring
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
StandardStepper :: StandardStepper() {
}
StandardStepper :: StandardStepper(uint8_t axis_index, uint8_t step_pin, uint8_t dir_pin, uint8_t disable_pin) {
type_id = STANDARD_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < MAX_AXES ? 0 : 1; // 0 = primary 1 = ganged
this->step_pin = step_pin;
this->dir_pin = dir_pin;
this->disable_pin = disable_pin;
init();
}
void StandardStepper :: init() {
_homing_mask = 0;
is_active = true; // as opposed to NullMotors, this is a real motor
set_axis_name();
init_step_dir_pins();
config_message();
}
void StandardStepper :: init_step_dir_pins() {
// TODO Step pin, but RMT complicates things
_invert_step_pin = bit_istrue(step_invert_mask->get(), bit(axis_index));
pinMode(dir_pin, OUTPUT);
#ifdef USE_RMT_STEPS
rmtConfig.rmt_mode = RMT_MODE_TX;
rmtConfig.clk_div = 20;
rmtConfig.mem_block_num = 2;
rmtConfig.tx_config.loop_en = false;
rmtConfig.tx_config.carrier_en = false;
rmtConfig.tx_config.carrier_freq_hz = 0;
rmtConfig.tx_config.carrier_duty_percent = 50;
rmtConfig.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
rmtConfig.tx_config.idle_output_en = true;
#ifdef STEP_PULSE_DELAY
rmtItem[0].duration0 = STEP_PULSE_DELAY * 4;
#else
rmtItem[0].duration0 = 1;
#endif
rmtItem[0].duration1 = 4 * pulse_microseconds->get();
rmtItem[1].duration0 = 0;
rmtItem[1].duration1 = 0;
rmt_chan_num[axis_index][dual_axis_index] = sys_get_next_RMT_chan_num();
rmt_set_source_clk((rmt_channel_t)rmt_chan_num[axis_index][dual_axis_index], RMT_BASECLK_APB);
rmtConfig.channel = (rmt_channel_t)rmt_chan_num[axis_index][dual_axis_index];
rmtConfig.tx_config.idle_level = _invert_step_pin ? RMT_IDLE_LEVEL_HIGH : RMT_IDLE_LEVEL_LOW;
rmtConfig.gpio_num = gpio_num_t(step_pin); // c is a wacky lang
rmtItem[0].level0 = rmtConfig.tx_config.idle_level;
rmtItem[0].level1 = !rmtConfig.tx_config.idle_level;
rmt_config(&rmtConfig);
rmt_fill_tx_items(rmtConfig.channel, &rmtItem[0], rmtConfig.mem_block_num, 0);
#else
pinMode(step_pin, OUTPUT);
#endif // USE_RMT_STEPS
pinMode(disable_pin, OUTPUT);
}
void StandardStepper :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis standard stepper motor Step:%s Dir:%s Disable:%s",
_axis_name,
pinName(step_pin).c_str(),
pinName(dir_pin).c_str(),
pinName(disable_pin).c_str());
}
void StandardStepper :: set_direction_pins(uint8_t onMask) {
digitalWrite(dir_pin, (onMask & bit(axis_index)));
}
void StandardStepper :: set_disable(bool disable) {
digitalWrite(disable_pin, disable);
}

View File

@@ -0,0 +1,242 @@
/*
TrinamicDriverClass.cpp
This is used for Trinamic SPI controlled stepper motor drivers.
Part of Grbl_ESP32
2020 - Bart Dring
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include <TMCStepper.h>
#include "TrinamicDriverClass.h"
TrinamicDriver :: TrinamicDriver(uint8_t axis_index,
uint8_t step_pin,
uint8_t dir_pin,
uint8_t disable_pin,
uint8_t cs_pin,
uint16_t driver_part_number,
float r_sense,
int8_t spi_index) {
type_id = TRINAMIC_SPI_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < 6 ? 0 : 1; // 0 = primary 1 = ganged
_driver_part_number = driver_part_number;
_r_sense = r_sense;
this->step_pin = step_pin;
this->dir_pin = dir_pin;
this->disable_pin = disable_pin;
this->cs_pin = cs_pin;
this->spi_index = spi_index;
_homing_mode = TRINAMIC_HOMING_MODE;
_homing_mask = 0; // no axes homing
if (_driver_part_number == 2130)
tmcstepper = new TMC2130Stepper(cs_pin, _r_sense, spi_index);
else if (_driver_part_number == 5160)
tmcstepper = new TMC5160Stepper(cs_pin, _r_sense, spi_index);
else {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Trinamic unsupported p/n:%d", _driver_part_number);
return;
}
set_axis_name();
init_step_dir_pins(); // from StandardStepper
digitalWrite(cs_pin, HIGH);
pinMode(cs_pin, OUTPUT);
// use slower speed if I2S
if (cs_pin >= I2S_OUT_PIN_BASE)
tmcstepper->setSPISpeed(TRINAMIC_SPI_FREQ);
config_message();
// init() must be called later, after all TMC drivers have CS pins setup.
}
void TrinamicDriver :: init() {
SPI.begin(); // this will get called for each motor, but does not seem to hurt anything
tmcstepper->begin();
test(); // Try communicating with motor. Prints an error if there is a problem.
read_settings(); // pull info from settings
set_mode(false);
_homing_mask = 0;
is_active = true; // as opposed to NullMotors, this is a real motor
}
/*
This is the startup message showing the basic definition
*/
void TrinamicDriver :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis Trinamic TMC%d Step:%s Dir:%s CS:%s Disable:%s Index:%d",
_axis_name,
_driver_part_number,
pinName(step_pin).c_str(),
pinName(dir_pin).c_str(),
pinName(cs_pin).c_str(),
pinName(disable_pin).c_str(),
spi_index);
}
bool TrinamicDriver :: test() {
switch (tmcstepper->test_connection()) {
case 1:
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Trinamic driver test failed. Check connection", _axis_name);
return false;
case 2:
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Trinamic driver test failed. Check motor power", _axis_name);
return false;
default:
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Trinamic driver test passed", _axis_name);
return true;
}
}
/*
Read setting and send them to the driver. Called at init() and whenever related settings change
both are stored as float Amps, but TMCStepper library expects...
uint16_t run (mA)
float hold (as a percentage of run)
*/
void TrinamicDriver :: read_settings() {
uint16_t run_i_ma = (uint16_t)(axis_settings[axis_index]->run_current->get() * 1000.0);
float hold_i_percent;
if (axis_settings[axis_index]->run_current->get() == 0)
hold_i_percent = 0;
else {
hold_i_percent = axis_settings[axis_index]->hold_current->get() / axis_settings[axis_index]->run_current->get();
if (hold_i_percent > 1.0)
hold_i_percent = 1.0;
}
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Current run %d hold %f", _axis_name, run_i_ma, hold_i_percent);
tmcstepper->microsteps(axis_settings[axis_index]->microsteps->get());
tmcstepper->rms_current(run_i_ma, hold_i_percent);
}
void TrinamicDriver :: set_homing_mode(uint8_t homing_mask, bool isHoming) {
_homing_mask = homing_mask;
set_mode(isHoming);
}
/*
There are ton of settings. I'll start by grouping then into modes for now.
Many people will want quiet and stallgaurd homing. Stallguard only run in
Coolstep mode, so it will need to switch to Coolstep when homing
*/
void TrinamicDriver :: set_mode(bool isHoming) {
if (isHoming)
_mode = TRINAMIC_HOMING_MODE;
else
_mode = TRINAMIC_RUN_MODE;
if (_lastMode == _mode)
return;
_lastMode = _mode;
switch (_mode) {
case TRINAMIC_MODE_STEALTHCHOP:
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_STEALTHCHOP");
tmcstepper->en_pwm_mode(true);
tmcstepper->pwm_autoscale(true);
tmcstepper->diag1_stall(false);
break;
case TRINAMIC_MODE_COOLSTEP:
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_COOLSTEP");
tmcstepper->en_pwm_mode(false);
tmcstepper->pwm_autoscale(false);
tmcstepper->TCOOLTHRS(NORMAL_TCOOLTHRS); // when to turn on coolstep
tmcstepper->THIGH(NORMAL_THIGH);
break;
case TRINAMIC_MODE_STALLGUARD:
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_STALLGUARD");
tmcstepper->en_pwm_mode(false);
tmcstepper->pwm_autoscale(false);
tmcstepper->TCOOLTHRS(calc_tstep(homing_feed_rate->get(), 150.0));
tmcstepper->THIGH(calc_tstep(homing_feed_rate->get(), 60.0));
tmcstepper->sfilt(1);
tmcstepper->diag1_stall(true); // stallguard i/o is on diag1
tmcstepper->sgt(axis_settings[axis_index]->stallguard->get());
break;
default:
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TRINAMIC_MODE_UNDEFINED");
}
}
/*
This is the stallguard tuning info. It is call debug, so it could be generic across all classes.
*/
void TrinamicDriver :: debug_message() {
uint32_t tstep = tmcstepper->TSTEP();
if (tstep == 0xFFFFF || tstep < 1) // if axis is not moving return
return;
float feedrate = st_get_realtime_rate(); //* settings.microsteps[axis_index] / 60.0 ; // convert mm/min to Hz
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Stallguard %d SG_Val: %04d Rate: %05.0f mm/min SG_Setting:%d",
_axis_name,
tmcstepper->stallguard(),
tmcstepper->sg_result(),
feedrate,
axis_settings[axis_index]->stallguard->get());
}
// calculate a tstep from a rate
// tstep = TRINAMIC_FCLK / (time between 1/256 steps)
// This is used to set the stallguard window from the homing speed.
// The percent is the offset on the window
uint32_t TrinamicDriver :: calc_tstep(float speed, float percent) {
float tstep = speed / 60.0 * axis_settings[axis_index]->steps_per_mm->get() * (float)(256 / axis_settings[axis_index]->microsteps->get());
tstep = TRINAMIC_FCLK / tstep * percent / 100.0;
return (uint32_t)tstep;
}
// this can use the enable feature over SPI. The dedicated pin must be in the enable mode,
// but that can be hardwired that way.
void TrinamicDriver :: set_disable(bool disable) {
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "%s Axis disable %d", _axis_name, disable);
digitalWrite(disable_pin, disable);
#ifdef USE_TRINAMIC_ENABLE
if (disable)
tmcstepper->toff(TRINAMIC_TOFF_DISABLE);
else {
if (_mode == TRINAMIC_MODE_STEALTHCHOP)
tmcstepper->toff(TRINAMIC_TOFF_STEALTHCHOP);
else
tmcstepper->toff(TRINAMIC_TOFF_COOLSTEP);
}
#endif
// the pin based enable could be added here.
// This would be for individual motors, not the single pin for all motors.
}

View File

@@ -0,0 +1,64 @@
/*
TrinamicDriverClass.h
Part of Grbl_ESP32
2020 - Bart Dring
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#define TRINAMIC_MODE_STEALTHCHOP 0 // very quiet
#define TRINAMIC_MODE_COOLSTEP 1 // everything runs cooler so higher current possible
#define TRINAMIC_MODE_STALLGUARD 2 // coolstep plus generates stall indication
#define NORMAL_TCOOLTHRS 0xFFFFF // 20 bit is max
#define NORMAL_THIGH 0
#define TMC2130_RSENSE_DEFAULT 0.11f
#define TMC5160_RSENSE_DEFAULT 0.075f
#define TRINAMIC_SPI_FREQ 100000
#define TRINAMIC_FCLK 12700000.0 // Internal clock Approx (Hz) used to calculate TSTEP from homing rate
// ==== defaults OK to define them in your machine definition ====
#ifndef TRINAMIC_RUN_MODE
#define TRINAMIC_RUN_MODE TRINAMIC_MODE_COOLSTEP
#endif
#ifndef TRINAMIC_HOMING_MODE
#define TRINAMIC_HOMING_MODE TRINAMIC_RUN_MODE
#endif
#ifndef TRINAMIC_TOFF_DISABLE
#define TRINAMIC_TOFF_DISABLE 0
#endif
#ifndef TRINAMIC_TOFF_STEALTHCHOP
#define TRINAMIC_TOFF_STEALTHCHOP 5
#endif
#ifndef TRINAMIC_TOFF_COOLSTEP
#define TRINAMIC_TOFF_COOLSTEP 3
#endif
#ifndef TRINAMICDRIVERCLASS_H
#define TRINAMICDRIVERCLASS_H
#include "MotorClass.h"
#include <TMCStepper.h> // https://github.com/teemuatlut/TMCStepper
#endif

View File

@@ -0,0 +1,146 @@
UnipolarMotor :: UnipolarMotor() {
}
UnipolarMotor :: UnipolarMotor(uint8_t axis_index, uint8_t pin_phase0, uint8_t pin_phase1, uint8_t pin_phase2, uint8_t pin_phase3) {
type_id = UNIPOLAR_MOTOR;
this->axis_index = axis_index % MAX_AXES;
this->dual_axis_index = axis_index < MAX_AXES ? 0 : 1; // 0 = primary 1 = ganged
_pin_phase0 = pin_phase0;
_pin_phase1 = pin_phase1;
_pin_phase2 = pin_phase2;
_pin_phase3 = pin_phase3;
_half_step = true; // TODO read from settings ... microstep > 1 = half step
set_axis_name();
init();
config_message();
}
void UnipolarMotor :: init() {
pinMode(_pin_phase0, OUTPUT);
pinMode(_pin_phase1, OUTPUT);
pinMode(_pin_phase2, OUTPUT);
pinMode(_pin_phase3, OUTPUT);
_current_phase = 0;
}
void UnipolarMotor :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"%s Axis unipolar stepper motor Ph0:%s Ph1:%s Ph2:%s Ph3:%s",
_axis_name,
pinName(_pin_phase0).c_str(),
pinName(_pin_phase1).c_str(),
pinName(_pin_phase2).c_str(),
pinName(_pin_phase3).c_str());
}
void UnipolarMotor :: set_disable(bool disable) {
if (disable) {
digitalWrite(_pin_phase0, 0);
digitalWrite(_pin_phase1, 0);
digitalWrite(_pin_phase2, 0);
digitalWrite(_pin_phase3, 0);
}
_enabled = !disable;
}
void UnipolarMotor::step(uint8_t step_mask, uint8_t dir_mask) {
uint8_t _phase[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // temporary phase values...all start as off
uint8_t phase_max;
if (!(step_mask & bit(axis_index)))
return; // a step is not required on this interrupt
if (!_enabled)
return; // don't do anything, phase is not changed or lost
if (_half_step)
phase_max = 7;
else
phase_max = 3;
if (dir_mask & bit(axis_index)) { // count up
if (_current_phase == phase_max)
_current_phase = 0;
else
_current_phase++;
} else { // count down
if (_current_phase == 0)
_current_phase = phase_max;
else
_current_phase--;
}
/*
8 Step : A AB B BC C CD D DA
4 Step : AB BC CD DA
Step IN4 IN3 IN2 IN1
A 0 0 0 1
AB 0 0 1 1
B 0 0 1 0
BC 0 1 1 0
C 0 1 0 0
CD 1 1 0 0
D 1 0 0 0
DA 1 0 0 1
*/
if (_half_step) {
switch (_current_phase) {
case 0:
_phase[0] = 1;
break;
case 1:
_phase[0] = 1;
_phase[1] = 1;
break;
case 2:
_phase[1] = 1;
break;
case 3:
_phase[1] = 1;
_phase[2] = 1;
break;
case 4:
_phase[2] = 1;
break;
case 5:
_phase[2] = 1;
_phase[3] = 1;
break;
case 6:
_phase[3] = 1;
break;
case 7:
_phase[3] = 1;
_phase[0] = 1;
break;
}
} else {
switch (_current_phase) {
case 0:
_phase[0] = 1;
_phase[1] = 1;
break;
case 1:
_phase[1] = 1;
_phase[2] = 1;
break;
case 2:
_phase[2] = 1;
_phase[3] = 1;
break;
case 3:
_phase[3] = 1;
_phase[0] = 1;
break;
}
}
digitalWrite(_pin_phase0, _phase[0]);
digitalWrite(_pin_phase1, _phase[1]);
digitalWrite(_pin_phase2, _phase[2]);
digitalWrite(_pin_phase3, _phase[3]);
}

55
Grbl_Esp32/Pins.cpp Normal file
View File

@@ -0,0 +1,55 @@
#include "grbl.h"
#include "i2s_out.h"
String pinName(uint8_t pin) {
if (pin == UNDEFINED_PIN) {
return "None";
}
if (pin < I2S_OUT_PIN_BASE) {
return String("GPIO(") + pin + ")";
} else {
return String("I2SO(") + (pin - I2S_OUT_PIN_BASE) + ")";
}
}
// Even if USE_I2S_OUT is not defined, it is necessary to
// override the following functions, instead of allowing
// the weak aliases in the library to apply, because of
// the UNDEFINED_PIN check. That UNDEFINED_PIN behavior
// cleans up other code by eliminating ifdefs and checks.
void IRAM_ATTR digitalWrite(uint8_t pin, uint8_t val) {
if (pin == UNDEFINED_PIN) {
return;
}
if (pin < I2S_OUT_PIN_BASE) {
__digitalWrite(pin, val);
return;
}
#ifdef USE_I2S_OUT
i2s_out_write(pin - I2S_OUT_PIN_BASE, val);
#endif
}
void IRAM_ATTR pinMode(uint8_t pin, uint8_t mode) {
if (pin == UNDEFINED_PIN) {
return;
}
if (pin < I2S_OUT_PIN_BASE)
__pinMode(pin, mode);
// I2S out pins cannot be configured, hence there
// is nothing to do here for them.
}
int IRAM_ATTR digitalRead(uint8_t pin) {
if (pin == UNDEFINED_PIN) {
return 0;
}
if (pin < I2S_OUT_PIN_BASE) {
return __digitalRead(pin);
}
#ifdef USE_I2S_OUT
return i2s_out_state(pin - I2S_OUT_PIN_BASE);
#else
return 0;
#endif
}

12
Grbl_Esp32/Pins.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include "Arduino.h"
#define UNDEFINED_PIN 255 // Can be used to show a pin has no i/O assigned
#define I2S_OUT_PIN_BASE 128
extern "C" int __digitalRead(uint8_t pin);
extern "C" void __pinMode(uint8_t pin, uint8_t mode);
extern "C" void __digitalWrite(uint8_t pin, uint8_t val);
String pinName(uint8_t pin);

View File

@@ -0,0 +1,624 @@
#include "grbl.h"
#include <map>
// WG Readable and writable as guest
// WU Readable and writable as user and admin
// WA Readable as user and admin, writable as admin
// If authentication is disabled, auth_level will be LEVEL_ADMIN
bool auth_failed(Word* w, const char* value, auth_t auth_level) {
permissions_t permissions = w->getPermissions();
switch (auth_level) {
case LEVEL_ADMIN: // Admin can do anything
return false; // Nothing is an Admin auth fail
case LEVEL_GUEST: // Guest can only access open settings
return permissions != WG; // Anything other than RG is Guest auth fail
case LEVEL_USER: // User is complicated...
if (!value) { // User can read anything
return false; // No read is a User auth fail
}
return permissions == WA; // User cannot write WA
default:
return true;
}
}
void show_setting(const char* name, const char* value, const char* description, ESPResponseStream* out) {
grbl_sendf(out->client(), "$%s=%s", name, value);
if (description) {
grbl_sendf(out->client(), " %s", description);
}
grbl_sendf(out->client(), "\r\n");
}
void settings_restore(uint8_t restore_flag) {
#ifdef WIFI_OR_BLUETOOTH
if (restore_flag & SETTINGS_RESTORE_WIFI_SETTINGS) {
#ifdef ENABLE_WIFI
wifi_config.reset_settings();
#endif
#ifdef ENABLE_BLUETOOTH
bt_config.reset_settings();
#endif
}
#endif
if (restore_flag & SETTINGS_RESTORE_DEFAULTS) {
for (Setting *s = Setting::List; s; s = s->next()) {
bool restore_startup = restore_flag & SETTINGS_RESTORE_STARTUP_LINES;
if (!s->getDescription()) {
const char *name = s->getName();
if (restore_startup || ((strcmp(name, "N0") != 0) && (strcmp(name, "N1") == 0))) {
s->setDefault();
}
}
}
}
if (restore_flag & SETTINGS_RESTORE_PARAMETERS) {
uint8_t idx;
float coord_data[N_AXIS];
memset(&coord_data, 0, sizeof(coord_data));
for (idx = 0; idx <= SETTING_INDEX_NCOORD; idx++) settings_write_coord_data(idx, coord_data);
}
if (restore_flag & SETTINGS_RESTORE_BUILD_INFO) {
EEPROM.write(EEPROM_ADDR_BUILD_INFO, 0);
EEPROM.write(EEPROM_ADDR_BUILD_INFO + 1, 0); // Checksum
EEPROM.commit();
}
}
// Get settings values from non volatile storage into memory
void load_settings()
{
for (Setting *s = Setting::List; s; s = s->next()) {
s->load();
}
}
extern void make_settings();
extern void make_grbl_commands();
extern void make_web_settings();
void settings_init()
{
EEPROM.begin(EEPROM_SIZE);
make_settings();
make_web_settings();
make_grbl_commands();
load_settings();
}
// TODO Settings - jog may need to be special-cased in the parser, since
// it is not really a setting and the entire line needs to be
// sent to gc_execute_line. It is probably also more time-critical
// than actual settings, which change infrequently, so handling
// it early is probably prudent.
uint8_t jog_set(uint8_t *value, auth_t auth_level, ESPResponseStream* out) {
// Execute only if in IDLE or JOG states.
if (sys.state != STATE_IDLE && sys.state != STATE_JOG) return STATUS_IDLE_ERROR;
// restore the $J= prefix because gc_execute_line() expects it
#define MAXLINE 128
char line[MAXLINE];
strcpy(line, "$J=");
strncat(line, (char *)value, MAXLINE-strlen("$J=")-1);
return gc_execute_line(line, out->client()); // NOTE: $J= is ignored inside g-code parser and used to detect jog motions.
}
err_t show_grbl_help(const char* value, auth_t auth_level, ESPResponseStream* out) {
report_grbl_help(out->client());
return STATUS_OK;
}
err_t report_gcode(const char *value, auth_t auth_level, ESPResponseStream* out) {
report_gcode_modes(out->client());
return STATUS_OK;
}
void show_grbl_settings(ESPResponseStream* out, type_t type, bool wantAxis) {
for (Setting *s = Setting::List; s; s = s->next()) {
if (s->getType() == type && s->getGrblName()) {
bool isAxis = s->getAxis() != NO_AXIS;
// The following test could be expressed more succinctly with XOR,
// but is arguably clearer when written out
if ((wantAxis && isAxis) || (!wantAxis && !isAxis)) {
show_setting(s->getGrblName(), s->getCompatibleValue(), NULL, out);
}
}
}
}
err_t report_normal_settings(const char* value, auth_t auth_level, ESPResponseStream* out) {
show_grbl_settings(out, GRBL, false); // GRBL non-axis settings
show_grbl_settings(out, GRBL, true); // GRBL axis settings
return STATUS_OK;
}
err_t report_extended_settings(const char* value, auth_t auth_level, ESPResponseStream* out) {
show_grbl_settings(out, GRBL, false); // GRBL non-axis settings
show_grbl_settings(out, EXTENDED, false); // Extended non-axis settings
show_grbl_settings(out, GRBL, true); // GRBL axis settings
show_grbl_settings(out, EXTENDED, true); // Extended axis settings
return STATUS_OK;
}
err_t list_grbl_names(const char* value, auth_t auth_level, ESPResponseStream* out)
{
for (Setting *s = Setting::List; s; s = s->next()) {
const char* gn = s->getGrblName();
if (gn) {
grbl_sendf(out->client(), "$%s => $%s\r\n", gn, s->getName());
}
}
return STATUS_OK;
}
err_t list_settings(const char* value, auth_t auth_level, ESPResponseStream* out)
{
for (Setting *s = Setting::List; s; s = s->next()) {
const char *displayValue = auth_failed(s, value, auth_level)
? "<Authentication required>"
: s->getStringValue();
show_setting(s->getName(), displayValue, NULL, out);
}
return STATUS_OK;
}
err_t list_commands(const char* value, auth_t auth_level, ESPResponseStream* out)
{
for (Command *cp = Command::List; cp; cp = cp->next()) {
const char* name = cp->getName();
const char* oldName = cp->getGrblName();
if (oldName) {
grbl_sendf(out->client(), "$%s or $%s", name, oldName);
} else {
grbl_sendf(out->client(), "$%s", name);
}
const char* description = cp->getDescription();
if (description) {
grbl_sendf(out->client(), " =%s", description);
}
grbl_sendf(out->client(), "\r\n");
}
return STATUS_OK;
}
err_t toggle_check_mode(const char* value, auth_t auth_level, ESPResponseStream* out) {
// Perform reset when toggling off. Check g-code mode should only work if Grbl
// is idle and ready, regardless of alarm locks. This is mainly to keep things
// simple and consistent.
if (sys.state == STATE_CHECK_MODE) {
mc_reset();
report_feedback_message(MESSAGE_DISABLED);
} else {
if (sys.state) return (STATUS_IDLE_ERROR); // Requires no alarm mode.
sys.state = STATE_CHECK_MODE;
report_feedback_message(MESSAGE_ENABLED);
}
return STATUS_OK;
}
err_t disable_alarm_lock(const char* value, auth_t auth_level, ESPResponseStream* out) {
if (sys.state == STATE_ALARM) {
// Block if safety door is ajar.
if (system_check_safety_door_ajar())
return (STATUS_CHECK_DOOR);
report_feedback_message(MESSAGE_ALARM_UNLOCK);
sys.state = STATE_IDLE;
// Don't run startup script. Prevents stored moves in startup from causing accidents.
} // Otherwise, no effect.
return STATUS_OK;
}
err_t report_ngc(const char* value, auth_t auth_level, ESPResponseStream* out) {
report_ngc_parameters(out->client());
return STATUS_OK;
}
err_t home(int cycle) {
if (homing_enable->get() == false)
return (STATUS_SETTING_DISABLED);
if (system_check_safety_door_ajar())
return (STATUS_CHECK_DOOR); // Block if safety door is ajar.
sys.state = STATE_HOMING; // Set system state variable
mc_homing_cycle(cycle);
if (!sys.abort) { // Execute startup scripts after successful homing.
sys.state = STATE_IDLE; // Set to IDLE when complete.
st_go_idle(); // Set steppers to the settings idle state before returning.
if (cycle == HOMING_CYCLE_ALL) {
char line[128];
system_execute_startup(line);
}
}
return STATUS_OK;
}
err_t home_all(const char* value, auth_t auth_level, ESPResponseStream* out) {
return home(HOMING_CYCLE_ALL);
}
err_t home_x(const char* value, auth_t auth_level, ESPResponseStream* out) {
return home(HOMING_CYCLE_X);
}
err_t home_y(const char* value, auth_t auth_level, ESPResponseStream* out) {
return home(HOMING_CYCLE_Y);
}
err_t home_z(const char* value, auth_t auth_level, ESPResponseStream* out) {
return home(HOMING_CYCLE_Z);
}
err_t home_a(const char* value, auth_t auth_level, ESPResponseStream* out) {
return home(HOMING_CYCLE_A);
}
err_t home_b(const char* value, auth_t auth_level, ESPResponseStream* out) {
return home(HOMING_CYCLE_B);
}
err_t home_c(const char* value, auth_t auth_level, ESPResponseStream* out) {
return home(HOMING_CYCLE_C);
}
err_t sleep_grbl(const char* value, auth_t auth_level, ESPResponseStream* out) {
system_set_exec_state_flag(EXEC_SLEEP);
return STATUS_OK;
}
err_t get_report_build_info(const char* value, auth_t auth_level, ESPResponseStream* out) {
if (!value) {
char line[128];
settings_read_build_info(line);
report_build_info(line, out->client());
return STATUS_OK;
}
#ifdef ENABLE_BUILD_INFO_WRITE_COMMAND
settings_store_build_info(value);
return STATUS_OK;
#else
return STATUS_INVALID_STATEMENT;
#endif
}
err_t report_startup_lines(const char* value, auth_t auth_level, ESPResponseStream* out) {
report_startup_line(0, startup_line_0->get(), out->client());
report_startup_line(1, startup_line_1->get(), out->client());
return STATUS_OK;
}
std::map<const char*, uint8_t, cmp_str> restoreCommands = {
#ifdef ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS
{ "$", SETTINGS_RESTORE_DEFAULTS },
{ "settings", SETTINGS_RESTORE_DEFAULTS },
#endif
#ifdef ENABLE_RESTORE_EEPROM_CLEAR_PARAMETERS
{ "#", SETTINGS_RESTORE_PARAMETERS },
{ "gcode", SETTINGS_RESTORE_PARAMETERS },
#endif
#ifdef ENABLE_RESTORE_EEPROM_WIPE_ALL
{ "*", SETTINGS_RESTORE_ALL },
{ "all", SETTINGS_RESTORE_ALL },
#endif
{ "@", SETTINGS_RESTORE_WIFI_SETTINGS },
{ "wifi", SETTINGS_RESTORE_WIFI_SETTINGS },
};
err_t restore_settings(const char* value, auth_t auth_level, ESPResponseStream* out) {
if (!value) {
return STATUS_INVALID_STATEMENT;
}
auto it = restoreCommands.find(value);
if (it == restoreCommands.end()) {
return STATUS_INVALID_STATEMENT;
}
settings_restore(it->second);
return STATUS_OK;
}
err_t showState(const char* value, auth_t auth_level, ESPResponseStream* out) {
grbl_sendf(out->client(), "State 0x%x\r\n", sys.state);
return STATUS_OK;
}
err_t doJog(const char* value, auth_t auth_level, ESPResponseStream* out) {
// For jogging, you must give gc_execute_line() a line that
// begins with $J=. There are several ways we can get here,
// including $J, $J=xxx, [J]xxx. For any form other than
// $J without =, we reconstruct a $J= line for gc_execute_line().
if (!value) {
return STATUS_INVALID_STATEMENT;
}
char jogLine[LINE_BUFFER_SIZE];
strcpy(jogLine, "$J=");
strcat(jogLine, value);
return gc_execute_line(jogLine, out->client());
}
std::map<uint8_t, const char*> ErrorCodes = {
{ STATUS_OK , "No error", },
{ STATUS_EXPECTED_COMMAND_LETTER , "Expected GCodecommand letter", },
{ STATUS_BAD_NUMBER_FORMAT , "Bad GCode number format", },
{ STATUS_INVALID_STATEMENT , "Invalid $ statement", },
{ STATUS_NEGATIVE_VALUE , "Negative value", },
{ STATUS_SETTING_DISABLED , "Setting disabled", },
{ STATUS_SETTING_STEP_PULSE_MIN , "Step pulse too short", },
{ STATUS_SETTING_READ_FAIL , "Failed to read settings", },
{ STATUS_IDLE_ERROR , "Command requires idle state", },
{ STATUS_SYSTEM_GC_LOCK , "GCode cannot be executed in lock or alarm state", },
{ STATUS_SOFT_LIMIT_ERROR , "Soft limit error", },
{ STATUS_OVERFLOW , "Line too long", },
{ STATUS_MAX_STEP_RATE_EXCEEDED , "Max step rate exceeded", },
{ STATUS_CHECK_DOOR , "Check door", },
{ STATUS_LINE_LENGTH_EXCEEDED , "Startup line too long", },
{ STATUS_TRAVEL_EXCEEDED , "Max travel exceeded during jog", },
{ STATUS_INVALID_JOG_COMMAND , "Invalid jog command", },
{ STATUS_SETTING_DISABLED_LASER , "Laser mode requires PWM output", },
{ STATUS_GCODE_UNSUPPORTED_COMMAND , "Unsupported GCode command", },
{ STATUS_GCODE_MODAL_GROUP_VIOLATION , "Gcode modal group violation", },
{ STATUS_GCODE_UNDEFINED_FEED_RATE , "Gcode undefined feed rate", },
{ STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER , "Gcode command value not integer", },
{ STATUS_GCODE_AXIS_COMMAND_CONFLICT , "Gcode axis command conflict", },
{ STATUS_GCODE_WORD_REPEATED , "Gcode word repeated", },
{ STATUS_GCODE_NO_AXIS_WORDS , "Gcode no axis words", },
{ STATUS_GCODE_INVALID_LINE_NUMBER , "Gcode invalid line number", },
{ STATUS_GCODE_VALUE_WORD_MISSING , "Gcode value word missing", },
{ STATUS_GCODE_UNSUPPORTED_COORD_SYS , "Gcode unsupported coordinate system", },
{ STATUS_GCODE_G53_INVALID_MOTION_MODE , "Gcode G53 invalid motion mode", },
{ STATUS_GCODE_AXIS_WORDS_EXIST , "Gcode extra axis words", },
{ STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE , "Gcode no axis words in plane", },
{ STATUS_GCODE_INVALID_TARGET , "Gcode invalid target", },
{ STATUS_GCODE_ARC_RADIUS_ERROR , "Gcode arc radius error", },
{ STATUS_GCODE_NO_OFFSETS_IN_PLANE , "Gcode no offsets in plane", },
{ STATUS_GCODE_UNUSED_WORDS , "Gcode unused words", },
{ STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR , "Gcode G43 dynamic axis error", },
{ STATUS_GCODE_MAX_VALUE_EXCEEDED , "Gcode max value exceeded", },
{ STATUS_P_PARAM_MAX_EXCEEDED , "P param max exceeded", },
{ STATUS_SD_FAILED_MOUNT , "SD failed mount", },
{ STATUS_SD_FAILED_READ , "SD failed read", },
{ STATUS_SD_FAILED_OPEN_DIR , "SD failed to open directory", },
{ STATUS_SD_DIR_NOT_FOUND , "SD directory not found", },
{ STATUS_SD_FILE_EMPTY , "SD file empty", },
{ STATUS_SD_FILE_NOT_FOUND , "SD file not found", },
{ STATUS_SD_FAILED_OPEN_FILE , "SD failed to open file", },
{ STATUS_SD_FAILED_BUSY , "SD is busy", },
{ STATUS_SD_FAILED_DEL_DIR, "SD failed to delete directory", },
{ STATUS_SD_FAILED_DEL_FILE, "SD failed to delete file", },
{ STATUS_BT_FAIL_BEGIN , "Bluetooth failed to start", },
{ STATUS_WIFI_FAIL_BEGIN , "WiFi failed to start", },
{ STATUS_NUMBER_RANGE , "Number out of range for setting", },
{ STATUS_INVALID_VALUE , "Invalid value for setting", },
{ STATUS_MESSAGE_FAILED , "Failed to send message", },
{ STATUS_NVS_SET_FAILED , "Failed to store setting", },
{ STATUS_AUTHENTICATION_FAILED, "Authentication failed!", },
};
const char* errorString(err_t errorNumber) {
auto it = ErrorCodes.find(errorNumber);
return it == ErrorCodes.end() ? NULL : it->second;
}
err_t listErrorCodes(const char* value, auth_t auth_level, ESPResponseStream* out) {
if (value) {
char* endptr = NULL;
uint8_t errorNumber = strtol(value, &endptr, 10);
if (*endptr) {
grbl_sendf(out->client(), "Malformed error number: %s\r\n", value);
return STATUS_INVALID_VALUE;
}
const char* errorName = errorString(errorNumber);
if (errorName) {
grbl_sendf(out->client(), "%d: %s\r\n", errorNumber, errorName);
return STATUS_OK;
} else {
grbl_sendf(out->client(), "Unknown error number: %d\r\n", errorNumber);
return STATUS_INVALID_VALUE;
}
}
for (auto it = ErrorCodes.begin();
it != ErrorCodes.end();
it++) {
grbl_sendf(out->client(), "%d: %s\r\n", it->first, it->second);
}
return STATUS_OK;
}
// Commands use the same syntax as Settings, but instead of setting or
// displaying a persistent value, a command causes some action to occur.
// That action could be anything, from displaying a run-time parameter
// to performing some system state change. Each command is responsible
// for decoding its own value string, if it needs one.
void make_grbl_commands() {
new GrblCommand("", "Help", show_grbl_help, ANY_STATE);
new GrblCommand("T", "State", showState, ANY_STATE);
new GrblCommand("J", "Jog", doJog, IDLE_OR_JOG);
new GrblCommand("$", "GrblSettings/List", report_normal_settings, NOT_CYCLE_OR_HOLD);
new GrblCommand("+", "ExtendedSettings/List", report_extended_settings, NOT_CYCLE_OR_HOLD);
new GrblCommand("L", "GrblNames/List", list_grbl_names, NOT_CYCLE_OR_HOLD);
new GrblCommand("S", "Settings/List", list_settings, NOT_CYCLE_OR_HOLD);
new GrblCommand("CMD", "Commands/List", list_commands, NOT_CYCLE_OR_HOLD);
new GrblCommand("E", "ErrorCodes/List",listErrorCodes, ANY_STATE);
new GrblCommand("G", "GCode/Modes", report_gcode, ANY_STATE);
new GrblCommand("C", "GCode/Check", toggle_check_mode, ANY_STATE);
new GrblCommand("X", "Alarm/Disable", disable_alarm_lock, ANY_STATE);
new GrblCommand("NVX", "Settings/Erase", Setting::eraseNVS, IDLE_OR_ALARM, WA);
new GrblCommand("V", "Settings/Stats", Setting::report_nvs_stats, IDLE_OR_ALARM);
new GrblCommand("#", "GCode/Offsets", report_ngc, IDLE_OR_ALARM);
new GrblCommand("H", "Home", home_all, IDLE_OR_ALARM);
#ifdef HOMING_SINGLE_AXIS_COMMANDS
new GrblCommand("HX", "Home/X", home_x, IDLE_OR_ALARM);
new GrblCommand("HY", "Home/Y", home_y, IDLE_OR_ALARM);
new GrblCommand("HZ", "Home/Z", home_z, IDLE_OR_ALARM);
#if (N_AXIS > 3)
new GrblCommand("HA", "Home/A", home_a, IDLE_OR_ALARM);
#endif
#if (N_AXIS > 4)
new GrblCommand("HB", "Home/B", home_b, IDLE_OR_ALARM);
#endif
#if (N_AXIS > 5)
new GrblCommand("HC", "Home/C", home_c, IDLE_OR_ALARM);
#endif
#endif
new GrblCommand("SLP", "System/Sleep", sleep_grbl, IDLE_OR_ALARM);
new GrblCommand("I", "Build/Info", get_report_build_info, IDLE_OR_ALARM);
new GrblCommand("N", "GCode/StartupLines", report_startup_lines, IDLE_OR_ALARM);
new GrblCommand("RST", "Settings/Restore", restore_settings, IDLE_OR_ALARM, WA);
};
// normalize_key puts a key string into canonical form -
// without whitespace.
// start points to a null-terminated string.
// Returns the first substring that does not contain whitespace.
// Case is unchanged because comparisons are case-insensitive.
char *normalize_key(char *start) {
char c;
// In the usual case, this loop will exit on the very first test,
// because the first character is likely to be non-white.
// Null ('\0') is not considered to be a space character.
while (isspace(c = *start) && c != '\0') {
++start;
}
// start now points to either a printable character or end of string
if (c == '\0') {
return start;
}
// Having found the beginning of the printable string,
// we now scan forward until we find a space character.
char *end;
for (end = start; (c = *end) != '\0' && !isspace(c); end++) {
}
// end now points to either a whitespace character of end of string
// In either case it is okay to place a null there
*end = '\0';
return start;
}
// This is the handler for all forms of settings commands,
// $..= and [..], with and without a value.
err_t do_command_or_setting(const char *key, char *value, auth_t auth_level, ESPResponseStream* out) {
// If value is NULL, it means that there was no value string, i.e.
// $key without =, or [key] with nothing following.
// If value is not NULL, but the string is empty, that is the form
// $key= with nothing following the = . It is important to distinguish
// those cases so that you can say "$N0=" to clear a startup line.
// First search the settings list by text name. If found, set a new
// value if one is given, otherwise display the current value
for (Setting *s = Setting::List; s; s = s->next()) {
if (strcasecmp(s->getName(), key) == 0) {
if (auth_failed(s, value, auth_level)) {
return STATUS_AUTHENTICATION_FAILED;
}
if (value) {
return s->setStringValue(value);
} else {
show_setting(s->getName(), s->getStringValue(), NULL, out);
return STATUS_OK;
}
}
}
// Then search the setting list by compatible name. If found, set a new
// value if one is given, otherwise display the current value in compatible mode
for (Setting *s = Setting::List; s; s = s->next()) {
if (s->getGrblName() && strcasecmp(s->getGrblName(), key) == 0) {
if (auth_failed(s, value, auth_level)) {
return STATUS_AUTHENTICATION_FAILED;
}
if (value) {
return s->setStringValue(value);
} else {
show_setting(s->getGrblName(), s->getCompatibleValue(), NULL, out);
return STATUS_OK;
}
}
}
// If we did not find a setting, look for a command. Commands
// handle values internally; you cannot determine whether to set
// or display solely based on the presence of a value.
for (Command *cp = Command::List; cp; cp = cp->next()) {
if ( (strcasecmp(cp->getName(), key) == 0)
|| (cp->getGrblName()
&& strcasecmp(cp->getGrblName(), key) == 0
)
) {
if (auth_failed(cp, value, auth_level)) {
return STATUS_AUTHENTICATION_FAILED;
}
return cp->action(value, auth_level, out);
}
}
// If we did not find an exact match and there is no value,
// indicating a display operation, we allow partial matches
// and display every possibility. This only applies to the
// text form of the name, not to the nnn and ESPnnn forms.
err_t retval = STATUS_INVALID_STATEMENT;
if (!value) {
auto lcKey = String(key);
// We allow the key string to begin with *, which we remove.
// This lets us look at X axis settings with $*x.
// $x by itself is the disable alarm lock command
if (lcKey.startsWith("*")) {
lcKey.remove(0,1);
}
lcKey.toLowerCase();
bool found = false;
for (Setting *s = Setting::List; s; s = s->next()) {
auto lcTest = String(s->getName());
lcTest.toLowerCase();
if (lcTest.indexOf(lcKey) >= 0) {
const char *displayValue = auth_failed(s, value, auth_level)
? "<Authentication required>"
: s->getStringValue();
show_setting(s->getName(), displayValue, NULL, out);
found = true;
}
}
if (found) {
return STATUS_OK;
}
}
return STATUS_INVALID_STATEMENT;
}
uint8_t system_execute_line(char* line, ESPResponseStream* out, auth_t auth_level) {
remove_password(line, auth_level);
char *value;
if (*line++ == '[') { // [ESPxxx] form
value = strrchr(line, ']');
if (!value) {
// Missing ] is an error in this form
return STATUS_INVALID_STATEMENT;
}
// ']' was found; replace it with null and set value to the rest of the line.
*value++ = '\0';
// If the rest of the line is empty, replace value with NULL.
if (*value == '\0') {
value = NULL;
}
} else {
// $xxx form
value = strchr(line, '=');
if (value) {
// $xxx=yyy form.
*value++ = '\0';
}
}
char *key = normalize_key(line);
// At this point there are three possibilities for value
// NULL - $xxx without =
// NULL - [ESPxxx] with nothing after ]
// empty string - $xxx= with nothing after
// non-empty string - [ESPxxx]yyy or $xxx=yyy
return do_command_or_setting(key, value, auth_level, out);
}
uint8_t system_execute_line(char* line, uint8_t client, auth_t auth_level) {
return system_execute_line(line, new ESPResponseStream(client, true), auth_level);
}
void system_execute_startup(char* line) {
err_t status_code;
char gcline[256];
strncpy(gcline, startup_line_0->get(), 255);
if (*gcline) {
status_code = gc_execute_line(gcline, CLIENT_SERIAL);
report_execute_startup_message(gcline, status_code, CLIENT_SERIAL);
}
strncpy(gcline, startup_line_1->get(), 255);
if (*gcline) {
status_code = gc_execute_line(gcline, CLIENT_SERIAL);
report_execute_startup_message(gcline, status_code, CLIENT_SERIAL);
}
}

View File

@@ -0,0 +1,613 @@
#include "grbl.h"
#include "JSONencoder.h"
#include <map>
#include "nvs.h"
Word::Word(type_t type, permissions_t permissions, const char* description, const char* grblName, const char* fullName)
: _description(description)
, _grblName(grblName)
, _fullName(fullName)
, _type(type)
, _permissions(permissions)
{}
Command* Command::List = NULL;
Command::Command(const char* description, type_t type, permissions_t permissions, const char* grblName, const char* fullName)
: Word(type, permissions, description, grblName, fullName)
{
link = List;
List = this;
}
Setting* Setting::List = NULL;
Setting::Setting(const char* description, type_t type, permissions_t permissions, const char* grblName, const char* fullName, bool (*checker)(char *))
: Word(type, permissions, description, grblName, fullName)
, _checker(checker)
{
link = List;
List = this;
// NVS keys are limited to 15 characters, so if the setting name is longer
// than that, we derive a 15-character name from a hash function
size_t len = strlen(fullName);
if (len <= 15) {
_keyName = _fullName;
} else {
// This is Donald Knuth's hash function from Vol 3, chapter 6.4
char *hashName = (char *)malloc(16);
uint32_t hash = len;
for (const char *s = fullName; *s; s++) {
hash = ((hash << 5) ^ (hash >> 27)) ^ (*s);
}
sprintf(hashName, "%.7s%08x", fullName, hash);
_keyName = hashName;
}
}
err_t Setting::check(char *s) {
if (sys.state != STATE_IDLE && !(sys.state & STATE_ALARM)) {
return STATUS_IDLE_ERROR;
}
if (!_checker) {
return STATUS_OK;
}
return _checker(s) ? STATUS_OK : STATUS_INVALID_VALUE;
}
nvs_handle Setting::_handle = 0;
void Setting::init() {
if (!_handle) {
if (esp_err_t err = nvs_open("Grbl_ESP32", NVS_READWRITE, &_handle)) {
grbl_sendf(CLIENT_SERIAL, "nvs_open failed with error %d\r\n", err);
}
}
}
IntSetting::IntSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, int32_t defVal, int32_t minVal, int32_t maxVal, bool (*checker)(char *) = NULL)
: Setting(description, type, permissions, grblName, name, checker)
, _defaultValue(defVal)
, _currentValue(defVal)
, _minValue(minVal)
, _maxValue(maxVal)
{ }
void IntSetting::load() {
esp_err_t err = nvs_get_i32(_handle, _keyName, &_storedValue);
if (err) {
_storedValue = std::numeric_limits<int32_t>::min();
_currentValue = _defaultValue;
} else {
_currentValue = _storedValue;
}
}
void IntSetting::setDefault() {
_currentValue = _defaultValue;
if (_storedValue != _currentValue) {
nvs_erase_key(_handle, _keyName);
}
}
err_t IntSetting::setStringValue(char* s) {
s = trim(s);
if (err_t err = check(s)) {
return err;
}
char* endptr;
int32_t convertedValue = strtol(s, &endptr, 10);
if (endptr == s || *endptr != '\0') {
return STATUS_BAD_NUMBER_FORMAT;
}
if (convertedValue < _minValue || convertedValue > _maxValue) {
return STATUS_NUMBER_RANGE;
}
_currentValue = convertedValue;
if (_storedValue != _currentValue) {
if (_currentValue == _defaultValue) {
nvs_erase_key(_handle, _keyName);
} else {
if (nvs_set_i32(_handle, _keyName, _currentValue)) {
return STATUS_NVS_SET_FAILED;
}
_storedValue = _currentValue;
}
}
return STATUS_OK;
}
const char* IntSetting::getStringValue() {
static char strval[32];
sprintf(strval, "%d", get());
return strval;
}
void IntSetting::addWebui(JSONencoder *j) {
if (getDescription()) {
j->begin_webui(getName(), getDescription(), "I", getStringValue(), _minValue, _maxValue);
j->end_object();
}
}
AxisMaskSetting::AxisMaskSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, int32_t defVal, bool (*checker)(char *) = NULL)
: Setting(description, type, permissions, grblName, name, checker)
, _defaultValue(defVal)
, _currentValue(defVal)
{ }
void AxisMaskSetting::load() {
esp_err_t err = nvs_get_i32(_handle, _keyName, &_storedValue);
if (err) {
_storedValue = -1;
_currentValue = _defaultValue;
} else {
_currentValue = _storedValue;
}
}
void AxisMaskSetting::setDefault() {
_currentValue = _defaultValue;
if (_storedValue != _currentValue) {
nvs_erase_key(_handle, _keyName);
}
}
err_t AxisMaskSetting::setStringValue(char* s) {
s = trim(s);
if (err_t err = check(s)) {
return err;
}
int32_t convertedValue;
char* endptr;
if (*s == '\0') {
convertedValue = 0;
} else {
convertedValue = strtol(s, &endptr, 10);
if (endptr == s || *endptr != '\0') {
// Try to convert as an axis list
convertedValue = 0;
auto axisNames = String("XYZABC");
while (*s) {
int index = axisNames.indexOf(toupper(*s++));
if (index < 0) {
return STATUS_BAD_NUMBER_FORMAT;
}
convertedValue |= bit(index);
}
}
}
_currentValue = convertedValue;
if (_storedValue != _currentValue) {
if (_currentValue == _defaultValue) {
nvs_erase_key(_handle, _keyName);
} else {
if (nvs_set_i32(_handle, _keyName, _currentValue)) {
return STATUS_NVS_SET_FAILED;
}
_storedValue = _currentValue;
}
}
return STATUS_OK;
}
const char* AxisMaskSetting::getCompatibleValue() {
static char strval[32];
sprintf(strval, "%d", get());
return strval;
}
const char* AxisMaskSetting::getStringValue() {
static char strval[32];
char *s = strval;
uint32_t mask = get();
for (int i = 0; i < MAX_N_AXIS; i++) {
if (mask & bit(i)) {
*s++ = "XYZABC"[i];
}
}
*s = '\0';
return strval;
}
void AxisMaskSetting::addWebui(JSONencoder *j) {
if (getDescription()) {
j->begin_webui(getName(), getDescription(), "I", getStringValue(), 0, (1<<MAX_N_AXIS)-1);
j->end_object();
}
}
FloatSetting::FloatSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, float defVal, float minVal, float maxVal, bool (*checker)(char *) = NULL)
: Setting(description, type, permissions, grblName, name, checker)
, _defaultValue(defVal)
, _currentValue(defVal)
, _minValue(minVal)
, _maxValue(maxVal)
{ }
void FloatSetting::load() {
union {
int32_t ival;
float fval;
} v;
if (nvs_get_i32(_handle, _keyName, &v.ival)) {
_currentValue = _defaultValue;
} else {
_currentValue = v.fval;
}
}
void FloatSetting::setDefault() {
_currentValue = _defaultValue;
if (_storedValue != _currentValue) {
nvs_erase_key(_handle, _keyName);
}
}
err_t FloatSetting::setStringValue(char* s) {
s = trim(s);
if (err_t err = check(s)) {
return err;
}
float convertedValue;
uint8_t len = strlen(s);
uint8_t retlen = 0;
if (!read_float(s, &retlen, &convertedValue)
|| retlen != len)
{
return STATUS_BAD_NUMBER_FORMAT;
}
if (convertedValue < _minValue || convertedValue > _maxValue) {
return STATUS_NUMBER_RANGE;
}
_currentValue = convertedValue;
if (_storedValue != _currentValue) {
if (_currentValue == _defaultValue) {
nvs_erase_key(_handle, _keyName);
} else {
union {
int32_t ival;
float fval;
} v;
v.fval = _currentValue;
if (nvs_set_i32(_handle, _keyName, v.ival)) {
return STATUS_NVS_SET_FAILED;
}
_storedValue = _currentValue;
}
}
return STATUS_OK;
}
const char* FloatSetting::getStringValue() {
static char strval[32];
(void)sprintf(strval, "%.3f", get());
#if 0
// With the goal of representing both large and small floating point
// numbers compactly while showing clearly that the are floating point,
// remove trailing zeros leaving at least one post-decimal digit.
// The loop is guaranteed to terminate because the string contains
// a decimal point which is not a '0'.
for (char *p = strval + strlen(strval) - 1; *p == '0'; --p) {
if (*(p-1) != '.' && *(p-1) != ',') {
*p = '\0';
}
}
#endif
return strval;
}
StringSetting::StringSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, const char* defVal, int min, int max, bool (*checker)(char *))
: Setting(description, type, permissions, grblName, name, checker)
{
_defaultValue = defVal;
_currentValue = defVal;
_minLength = min;
_maxLength = max;
};
void StringSetting::load() {
size_t len = 0;
esp_err_t err = nvs_get_str(_handle, _keyName, NULL, &len);
if(err) {
_storedValue = _defaultValue;
_currentValue = _defaultValue;
return;
}
char buf[len];
err = nvs_get_str(_handle, _keyName, buf, &len);
if (err) {
_storedValue = _defaultValue;
_currentValue = _defaultValue;
return;
}
_storedValue = String(buf);
_currentValue = _storedValue;
}
void StringSetting::setDefault() {
_currentValue = _defaultValue;
if (_storedValue != _currentValue) {
nvs_erase_key(_handle, _keyName);
}
}
err_t StringSetting::setStringValue(char* s) {
if (_minLength && _maxLength && (strlen(s) < _minLength || strlen(s) > _maxLength)) {
return STATUS_BAD_NUMBER_FORMAT;
}
if (err_t err = check(s)) {
return err;
}
_currentValue = s;
if (_storedValue != _currentValue) {
if (_currentValue == _defaultValue) {
nvs_erase_key(_handle, _keyName);
_storedValue = _defaultValue;
} else {
if (nvs_set_str(_handle, _keyName, _currentValue.c_str())) {
return STATUS_NVS_SET_FAILED;
}
_storedValue = _currentValue;
}
}
return STATUS_OK;
}
const char* StringSetting::getStringValue() {
// If the string is a password do not display it
if (_checker &&
(
#ifdef ENABLE_WIFI
_checker == (bool (*)(char *))WiFiConfig::isPasswordValid
||
#endif
_checker == (bool (*)(char *))COMMANDS::isLocalPasswordValid
)) {
return "******";
}
return _currentValue.c_str();
}
void StringSetting::addWebui(JSONencoder *j) {
if (!getDescription()) {
return;
}
j->begin_webui(
getName(), getDescription(), "S", getStringValue(), _minLength, _maxLength);
j->end_object();
}
typedef std::map<const char *, int8_t, cmp_str> enum_opt_t;
EnumSetting::EnumSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, int8_t defVal, enum_opt_t *opts)
// No checker function because enumerations have an exact set of value
: Setting(description, type, permissions, grblName, name, NULL)
, _defaultValue(defVal)
, _options(opts)
{ }
void EnumSetting::load() {
esp_err_t err = nvs_get_i8(_handle, _keyName, &_storedValue);
if (err) {
_storedValue = -1;
_currentValue = _defaultValue;
} else {
_currentValue = _storedValue;
}
}
void EnumSetting::setDefault() {
_currentValue = _defaultValue;
if (_storedValue != _currentValue) {
nvs_erase_key(_handle, _keyName);
}
}
// For enumerations, we allow the value to be set
// either with the string name or the numeric value.
// This is necessary for WebUI, which uses the number
// for setting.
err_t EnumSetting::setStringValue(char* s) {
s = trim(s);
enum_opt_t::iterator it = _options->find(s);
if (it == _options->end()) {
// If we don't find the value in keys, look for it in the numeric values
// Disallow empty string
if (!s || !*s) {
return STATUS_BAD_NUMBER_FORMAT;
}
char *endptr;
uint8_t num = strtol(s, &endptr, 10);
// Disallow non-numeric characters in string
if (*endptr) {
return STATUS_BAD_NUMBER_FORMAT;
}
for (it = _options->begin(); it != _options->end(); it++) {
if (it->second == num) {
break;
}
}
if (it == _options->end()) {
return STATUS_BAD_NUMBER_FORMAT;
}
}
_currentValue = it->second;
if (_storedValue != _currentValue) {
if (_storedValue == _defaultValue) {
nvs_erase_key(_handle, _keyName);
} else {
if (nvs_set_i8(_handle, _keyName, _currentValue)) {
return STATUS_NVS_SET_FAILED;
}
_storedValue = _currentValue;
}
}
return STATUS_OK;
}
const char* EnumSetting::getStringValue() {
for (enum_opt_t::iterator it = _options->begin();
it != _options->end();
it++) {
if (it->second == _currentValue) {
return it->first;
}
}
return "???";
}
void EnumSetting::addWebui(JSONencoder *j) {
if (!getDescription()) {
return;
}
j->begin_webui(getName(), getDescription(), "B", String(get()).c_str());
j->begin_array("O");
for (enum_opt_t::iterator it = _options->begin();
it != _options->end();
it++) {
j->begin_object();
j->member(it->first, it->second);
j->end_object();
}
j->end_array();
j->end_object();
}
FlagSetting::FlagSetting(const char *description, type_t type, permissions_t permissions, const char * grblName, const char* name, bool defVal, bool (*checker)(char *) = NULL) :
Setting(description, type, permissions, grblName, name, checker),
_defaultValue(defVal)
{ }
void FlagSetting::load() {
esp_err_t err = nvs_get_i8(_handle, _keyName, &_storedValue);
if (err) {
_storedValue = -1; // Neither well-formed false (0) nor true (1)
_currentValue = _defaultValue;
} else {
_currentValue = !!_storedValue;
}
}
void FlagSetting::setDefault() {
_currentValue = _defaultValue;
if (_storedValue != _currentValue) {
nvs_erase_key(_handle, _keyName);
}
}
err_t FlagSetting::setStringValue(char* s) {
s = trim(s);
_currentValue = (strcasecmp(s, "on") == 0)
|| (strcasecmp(s, "true") == 0)
|| (strcasecmp(s, "enabled") == 0)
|| (strcasecmp(s, "yes") == 0)
|| (strcasecmp(s, "1") == 0);
// _storedValue is -1, 0, or 1
// _currentValue is 0 or 1
if (_storedValue != (int8_t)_currentValue) {
if (_currentValue == _defaultValue) {
nvs_erase_key(_handle, _keyName);
} else {
if (nvs_set_i8(_handle, _keyName, _currentValue)) {
return STATUS_NVS_SET_FAILED;
}
_storedValue = _currentValue;
}
}
return STATUS_OK;
}
const char* FlagSetting::getStringValue() {
return get() ? "On" : "Off";
}
const char* FlagSetting::getCompatibleValue() {
return get() ? "1" : "0";
}
#include <WiFi.h>
IPaddrSetting::IPaddrSetting(const char *description, type_t type, permissions_t permissions, const char * grblName, const char* name, uint32_t defVal, bool (*checker)(char *) = NULL)
: Setting(description, type, permissions, grblName, name, checker) // There are no GRBL IP settings.
, _defaultValue(defVal)
, _currentValue(defVal)
{ }
IPaddrSetting::IPaddrSetting(const char *description, type_t type, permissions_t permissions, const char * grblName, const char* name, const char *defVal, bool (*checker)(char *) = NULL)
: Setting(description, type, permissions, grblName, name, checker)
{
IPAddress ipaddr;
if (ipaddr.fromString(defVal)) {
_defaultValue = ipaddr;
_currentValue = _defaultValue;
} else {
throw std::runtime_error("Bad IPaddr default");
}
}
void IPaddrSetting::load() {
esp_err_t err = nvs_get_i32(_handle, _keyName, (int32_t *)&_storedValue);
if (err) {
_storedValue = 0x000000ff; // Unreasonable value for any IP thing
_currentValue = _defaultValue;
} else {
_currentValue = _storedValue;
}
}
void IPaddrSetting::setDefault() {
_currentValue = _defaultValue;
if (_storedValue != _currentValue) {
nvs_erase_key(_handle, _keyName);
}
}
err_t IPaddrSetting::setStringValue(char* s) {
s = trim(s);
if (err_t err = check(s)) {
return err;
}
IPAddress ipaddr;
if (!ipaddr.fromString(s)) {
return STATUS_INVALID_VALUE;
}
_currentValue = ipaddr;
if (_storedValue != _currentValue) {
if (_currentValue == _defaultValue) {
nvs_erase_key(_handle, _keyName);
} else {
if (nvs_set_i32(_handle, _keyName, (int32_t)_currentValue)) {
return STATUS_NVS_SET_FAILED;
}
_storedValue = _currentValue;
}
}
return STATUS_OK;
}
const char* IPaddrSetting::getStringValue() {
static String s;
IPAddress ipaddr(get());
s = ipaddr.toString();
return s.c_str();
}
void IPaddrSetting::addWebui(JSONencoder *j) {
if (getDescription()) {
j->begin_webui(getName(), getDescription(), "A", getStringValue());
j->end_object();
}
}
AxisSettings::AxisSettings(const char *axisName) :
name(axisName)
{}
err_t GrblCommand::action(char* value, auth_t auth_type, ESPResponseStream* out) {
if (sys.state & _disallowedStates) {
return STATUS_IDLE_ERROR;
}
return _action((const char*)value, auth_type, out);
};

351
Grbl_Esp32/SettingsClass.h Normal file
View File

@@ -0,0 +1,351 @@
#pragma once
#include "JSONencoder.h"
#include <map>
#include <nvs.h>
#include "espresponse.h"
// Command::List is a linked list of all settings,
// so common code can enumerate them.
class Command;
// extern Command *CommandsList;
// This abstract class defines the generic interface that
// is used to set and get values for all settings independent
// of their underlying data type. The values are always
// represented as human-readable strings. This generic
// interface is used for managing settings via the user interface.
// Derived classes implement these generic functions for different
// kinds of data. Code that accesses settings should use only these
// generic functions and should not use derived classes directly.
enum {
NO_AXIS = 255,
};
typedef enum : uint8_t {
GRBL = 1, // Classic GRBL settings like $100
EXTENDED, // Settings added by early versions of Grbl_Esp32
WEBSET, // Settings for ESP3D_WebUI, stored in NVS
GRBLCMD, // Non-persistent GRBL commands like $H
WEBCMD, // ESP3D_WebUI commands that are not directly settings
} type_t;
typedef enum : uint8_t {
WG, // Readable and writable as guest
WU, // Readable and writable as user and admin
WA, // Readable as user and admin, writable as admin
} permissions_t;
typedef uint8_t axis_t;
class Word {
protected:
const char* _description;
const char* _grblName;
const char* _fullName;
type_t _type;
permissions_t _permissions;
public:
Word(type_t type, permissions_t permissions, const char *description, const char * grblName, const char* fullName);
type_t getType() { return _type; }
permissions_t getPermissions() { return _permissions; }
const char* getName() { return _fullName; }
const char* getGrblName() { return _grblName; }
const char* getDescription() { return _description; }
};
class Command : public Word {
protected:
Command *link; // linked list of setting objects
public:
static Command* List;
Command* next() { return link; }
~Command() {}
Command(const char *description, type_t type, permissions_t permissions, const char * grblName, const char* fullName);
// The default implementation of addWebui() does nothing.
// Derived classes may override it to do something.
virtual void addWebui(JSONencoder *) {};
virtual err_t action(char* value, auth_t auth_level, ESPResponseStream* out) =0;
};
class Setting : public Word {
private:
protected:
static nvs_handle _handle;
// group_t _group;
axis_t _axis = NO_AXIS;
Setting *link; // linked list of setting objects
bool (*_checker)(char *);
const char* _keyName;
public:
static void init();
static Setting* List;
Setting* next() { return link; }
err_t check(char *s);
static err_t report_nvs_stats(const char* value, auth_t auth_level, ESPResponseStream* out) {
nvs_stats_t stats;
if (err_t err = nvs_get_stats(NULL, &stats))
return err;
grbl_sendf(out->client(), "[MSG: NVS Used: %d Free: %d Total: %d]\r\n",
stats.used_entries, stats.free_entries, stats.total_entries);
#if 0 // The SDK we use does not have this yet
nvs_iterator_t it = nvs_entry_find(NULL, NULL, NVS_TYPE_ANY);
while (it != NULL) {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
it = nvs_entry_next(it);
grbl_sendf(out->client(), "namespace %s key '%s', type '%d' \n", info.namespace_name, info.key, info.type);
}
#endif
return STATUS_OK;
}
static err_t eraseNVS(const char* value, auth_t auth_level, ESPResponseStream* out) {
nvs_erase_all(_handle);
// return STATUS_OK;
return 0;
}
~Setting() {}
// Setting(const char *description, group_t group, const char * grblName, const char* fullName, bool (*checker)(char *));
Setting(const char *description, type_t type, permissions_t permissions, const char * grblName, const char* fullName, bool (*checker)(char *));
axis_t getAxis() { return _axis; }
void setAxis(axis_t axis) { _axis = axis; }
// load() reads the backing store to get the current
// value of the setting. This could be slow so it
// should be done infrequently, typically once at startup.
virtual void load() {};
virtual void setDefault() {};
// The default implementation of addWebui() does nothing.
// Derived classes may override it to do something.
virtual void addWebui(JSONencoder *) {};
virtual err_t setStringValue(char* value) =0;
err_t setStringValue(String s) { return setStringValue(s.c_str()); }
virtual const char* getStringValue() =0;
virtual const char* getCompatibleValue() { return getStringValue(); }
};
class IntSetting : public Setting {
private:
int32_t _defaultValue;
int32_t _currentValue;
int32_t _storedValue;
int32_t _minValue;
int32_t _maxValue;
public:
IntSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, int32_t defVal, int32_t minVal, int32_t maxVal, bool (*checker)(char *));
IntSetting(type_t type, permissions_t permissions, const char* grblName, const char* name, int32_t defVal, int32_t minVal, int32_t maxVal, bool (*checker)(char *) = NULL)
: IntSetting(NULL, type, permissions, grblName, name, defVal, minVal, maxVal, checker)
{ }
void load();
void setDefault();
void addWebui(JSONencoder *);
err_t setStringValue(char* value);
const char* getStringValue();
int32_t get() { return _currentValue; }
};
class AxisMaskSetting : public Setting {
private:
int32_t _defaultValue;
int32_t _currentValue;
int32_t _storedValue;
public:
AxisMaskSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, int32_t defVal, bool (*checker)(char *));
AxisMaskSetting(type_t type, permissions_t permissions, const char* grblName, const char* name, int32_t defVal, bool (*checker)(char *) = NULL)
: AxisMaskSetting(NULL, type, permissions, grblName, name, defVal, checker)
{ }
void load();
void setDefault();
void addWebui(JSONencoder *);
err_t setStringValue(char* value);
const char* getCompatibleValue();
const char* getStringValue();
int32_t get() { return _currentValue; }
};
class FloatSetting : public Setting {
private:
float _defaultValue;
float _currentValue;
float _storedValue;
float _minValue;
float _maxValue;
public:
FloatSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, float defVal, float minVal, float maxVal, bool (*checker)(char *));
FloatSetting(type_t type, permissions_t permissions, const char* grblName, const char* name, float defVal, float minVal, float maxVal, bool (*checker)(char *) = NULL)
: FloatSetting(NULL, type, permissions, grblName, name, defVal, minVal, maxVal, checker)
{ }
void load();
void setDefault();
// There are no Float settings in WebUI
void addWebui(JSONencoder *) {}
err_t setStringValue(char* value);
const char* getStringValue();
float get() { return _currentValue; }
};
#define MAX_SETTING_STRING 256
class StringSetting : public Setting {
private:
String _defaultValue;
String _currentValue;
String _storedValue;
int _minLength;
int _maxLength;
void _setStoredValue(const char *s);
public:
StringSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, const char* defVal, int min, int max, bool (*checker)(char *));
StringSetting(type_t type, permissions_t permissions, const char* grblName, const char* name, const char* defVal, bool (*checker)(char *) = NULL)
: StringSetting(NULL, type, permissions, grblName, name, defVal, 0, 0, checker)
{ };
void load();
void setDefault();
void addWebui(JSONencoder *);
err_t setStringValue(char* value);
const char* getStringValue();
const char* get() { return _currentValue.c_str(); }
};
struct cmp_str
{
bool operator()(char const *a, char const *b) const
{
return strcasecmp(a, b) < 0;
}
};
typedef std::map<const char *, int8_t, cmp_str> enum_opt_t;
class EnumSetting : public Setting {
private:
int8_t _defaultValue;
int8_t _storedValue;
int8_t _currentValue;
std::map<const char *, int8_t, cmp_str>* _options;
public:
EnumSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, int8_t defVal, enum_opt_t* opts);
EnumSetting(type_t type, permissions_t permissions, const char* grblName, const char* name, int8_t defVal, enum_opt_t* opts) :
EnumSetting(NULL, type, permissions, grblName, name, defVal, opts)
{ }
void load();
void setDefault();
void addWebui(JSONencoder *);
err_t setStringValue(char* value);
const char* getStringValue();
int8_t get() { return _currentValue; }
};
class FlagSetting : public Setting {
private:
bool _defaultValue;
int8_t _storedValue;
bool _currentValue;
public:
FlagSetting(const char *description, type_t type, permissions_t permissions, const char* grblName, const char* name, bool defVal, bool (*checker)(char *));
FlagSetting(type_t type, permissions_t permissions, const char* grblName, const char* name, bool defVal, bool (*checker)(char *) = NULL)
: FlagSetting(NULL, type, permissions, grblName, name, defVal, checker)
{ }
void load();
void setDefault();
// There are no Flag settings in WebUI
// The booleans are expressed as Enums
void addWebui(JSONencoder *) {}
err_t setStringValue(char* value);
const char* getCompatibleValue();
const char* getStringValue();
bool get() { return _currentValue; }
};
class IPaddrSetting : public Setting {
private:
uint32_t _defaultValue;
uint32_t _currentValue;
uint32_t _storedValue;
public:
IPaddrSetting(const char *description, type_t type, permissions_t permissions, const char * grblName, const char* name, uint32_t defVal, bool (*checker)(char *));
IPaddrSetting(const char *description, type_t type, permissions_t permissions, const char * grblName, const char* name, const char *defVal, bool (*checker)(char *));
void load();
void setDefault();
void addWebui(JSONencoder *);
err_t setStringValue(char* value);
const char* getStringValue();
uint32_t get() { return _currentValue; }
};
class AxisSettings {
public:
const char* name;
FloatSetting *steps_per_mm;
FloatSetting *max_rate;
FloatSetting *acceleration;
FloatSetting *max_travel;
FloatSetting *run_current;
FloatSetting *hold_current;
IntSetting *microsteps;
IntSetting *stallguard;
AxisSettings(const char *axisName);
};
class WebCommand : public Command {
private:
err_t (*_action)(char *, auth_t);
const char* password;
public:
WebCommand(const char* description, type_t type, permissions_t permissions, const char * grblName, const char* name, err_t (*action)(char *, auth_t)) :
Command(description, type, permissions, grblName, name),
_action(action)
{}
err_t action(char* value, auth_t auth_level, ESPResponseStream* response);
};
enum : uint8_t {
ANY_STATE = 0,
IDLE_OR_ALARM = 0xff & ~STATE_ALARM,
IDLE_OR_JOG = 0xff & ~STATE_JOG,
NOT_CYCLE_OR_HOLD = STATE_CYCLE | STATE_HOLD,
};
class GrblCommand : public Command {
private:
err_t (*_action)(const char *, auth_t, ESPResponseStream*);
uint8_t _disallowedStates;
public:
GrblCommand(const char * grblName, const char* name, err_t (*action)(const char*, auth_t, ESPResponseStream*), uint8_t disallowedStates, permissions_t auth)
: Command(NULL, GRBLCMD, auth, grblName, name)
, _action(action)
, _disallowedStates(disallowedStates)
{}
GrblCommand(const char * grblName, const char* name, err_t (*action)(const char*, auth_t, ESPResponseStream*), uint8_t disallowedStates)
: GrblCommand(grblName, name, action, disallowedStates, WG)
{}
err_t action(char* value, auth_t auth_level, ESPResponseStream* response);
};

View File

@@ -0,0 +1,316 @@
#include "grbl.h"
bool motorSettingChanged = false;
StringSetting* startup_line_0;
StringSetting* startup_line_1;
StringSetting* build_info;
IntSetting* pulse_microseconds;
IntSetting* stepper_idle_lock_time;
AxisMaskSetting* step_invert_mask;
AxisMaskSetting* dir_invert_mask;
// TODO Settings - need to call st_generate_step_invert_masks;
AxisMaskSetting* homing_dir_mask;
AxisMaskSetting* stallguard_debug_mask;
FlagSetting* step_enable_invert;
FlagSetting* limit_invert;
FlagSetting* probe_invert;
FlagSetting* report_inches;
FlagSetting* soft_limits;
// TODO Settings - need to check for HOMING_ENABLE
FlagSetting* hard_limits;
// TODO Settings - need to call limits_init;
FlagSetting* homing_enable;
// TODO Settings - also need to clear, but not set, soft_limits
FlagSetting* laser_mode;
// TODO Settings - also need to call my_spindle->init;
IntSetting* status_mask;
FloatSetting* junction_deviation;
FloatSetting* arc_tolerance;
FloatSetting* homing_feed_rate;
FloatSetting* homing_seek_rate;
FloatSetting* homing_debounce;
FloatSetting* homing_pulloff;
FloatSetting* spindle_pwm_freq;
FloatSetting* rpm_max;
FloatSetting* rpm_min;
FloatSetting* spindle_pwm_off_value;
FloatSetting* spindle_pwm_min_value;
FloatSetting* spindle_pwm_max_value;
IntSetting* spindle_pwm_bit_precision;
EnumSetting* spindle_type;
enum_opt_t spindleTypes = {
{ "NONE", SPINDLE_TYPE_NONE, },
{ "PWM", SPINDLE_TYPE_PWM, },
{ "RELAY", SPINDLE_TYPE_RELAY, },
{ "LASER", SPINDLE_TYPE_LASER, },
{ "DAC", SPINDLE_TYPE_DAC, },
{ "HUANYANG", SPINDLE_TYPE_HUANYANG, },
{ "BESC", SPINDLE_TYPE_BESC, },
{ "10V", SPINDLE_TYPE_10V, },
};
AxisSettings* x_axis_settings;
AxisSettings* y_axis_settings;
AxisSettings* z_axis_settings;
AxisSettings* a_axis_settings;
AxisSettings* b_axis_settings;
AxisSettings* c_axis_settings;
AxisSettings* axis_settings[MAX_N_AXIS];
typedef struct {
const char* name;
float steps_per_mm;
float max_rate;
float acceleration;
float max_travel;
float run_current;
float hold_current;
uint16_t microsteps;
uint16_t stallguard;
} axis_defaults_t;
axis_defaults_t axis_defaults[] = {
{
"X",
DEFAULT_X_STEPS_PER_MM,
DEFAULT_X_MAX_RATE,
DEFAULT_X_ACCELERATION,
DEFAULT_X_MAX_TRAVEL,
DEFAULT_X_CURRENT,
DEFAULT_X_HOLD_CURRENT,
DEFAULT_X_MICROSTEPS,
DEFAULT_X_STALLGUARD
},
{
"Y",
DEFAULT_Y_STEPS_PER_MM,
DEFAULT_Y_MAX_RATE,
DEFAULT_Y_ACCELERATION,
DEFAULT_Y_MAX_TRAVEL,
DEFAULT_Y_CURRENT,
DEFAULT_Y_HOLD_CURRENT,
DEFAULT_Y_MICROSTEPS,
DEFAULT_Y_STALLGUARD
},
{
"Z",
DEFAULT_Z_STEPS_PER_MM,
DEFAULT_Z_MAX_RATE,
DEFAULT_Z_ACCELERATION,
DEFAULT_Z_MAX_TRAVEL,
DEFAULT_Z_CURRENT,
DEFAULT_Z_HOLD_CURRENT,
DEFAULT_Z_MICROSTEPS,
DEFAULT_Z_STALLGUARD
},
{
"A",
DEFAULT_A_STEPS_PER_MM,
DEFAULT_A_MAX_RATE,
DEFAULT_A_ACCELERATION,
DEFAULT_A_MAX_TRAVEL,
DEFAULT_A_CURRENT,
DEFAULT_A_HOLD_CURRENT,
DEFAULT_A_MICROSTEPS,
DEFAULT_A_STALLGUARD
},
{
"B",
DEFAULT_B_STEPS_PER_MM,
DEFAULT_B_MAX_RATE,
DEFAULT_B_ACCELERATION,
DEFAULT_B_MAX_TRAVEL,
DEFAULT_B_CURRENT,
DEFAULT_B_HOLD_CURRENT,
DEFAULT_B_MICROSTEPS,
DEFAULT_B_STALLGUARD
},
{
"C",
DEFAULT_C_STEPS_PER_MM,
DEFAULT_C_MAX_RATE,
DEFAULT_C_ACCELERATION,
DEFAULT_C_MAX_TRAVEL,
DEFAULT_C_CURRENT,
DEFAULT_C_HOLD_CURRENT,
DEFAULT_C_MICROSTEPS,
DEFAULT_C_STALLGUARD
}
};
// Construct e.g. X_MAX_RATE from axisName "X" and tail "_MAX_RATE"
// in dynamically allocated memory that will not be freed.
static const char *makename(const char *axisName, const char *tail) {
char* retval = (char *)malloc(strlen(axisName) + strlen(tail) + 2);
strcpy(retval, axisName);
strcat(retval, "/");
return strcat(retval, tail);
}
static bool checkStartupLine(char* value) {
if (sys.state != STATE_IDLE)
return STATUS_IDLE_ERROR;
return gc_execute_line(value, CLIENT_SERIAL) == 0;
}
static bool checkStallguard(char* value) {
motorSettingChanged = true;
return true;
}
static bool checkMicrosteps(char* value) {
motorSettingChanged = true;
return true;
}
static bool checkRunCurrent(char* value) {
motorSettingChanged = true;
return true;
}
static bool checkHoldcurrent(char* value) {
motorSettingChanged = true;
return true;
}
static bool checkStallguardDebugMask(char* val) {
motorSettingChanged = true;
return true;
}
// Generates a string like "122" from axisNum 2 and base 120
static const char* makeGrblName(int axisNum, int base) {
// To omit A,B,C axes:
// if (axisNum > 2) return NULL;
char buf[4];
snprintf(buf, 4, "%d", axisNum + base);
char* retval = (char*)malloc(strlen(buf));
return strcpy(retval, buf);
}
void make_settings() {
Setting::init();
// Create the axis settings in the order that people are
// accustomed to seeing.
int axis;
axis_defaults_t* def;
for (axis = 0; axis < N_AXIS; axis++) {
def = &axis_defaults[axis];
axis_settings[axis] = new AxisSettings(def->name);
}
x_axis_settings = axis_settings[X_AXIS];
y_axis_settings = axis_settings[Y_AXIS];
z_axis_settings = axis_settings[Z_AXIS];
a_axis_settings = axis_settings[A_AXIS];
b_axis_settings = axis_settings[B_AXIS];
c_axis_settings = axis_settings[C_AXIS];
for (axis = N_AXIS - 1; axis >= 0; axis--) {
def = &axis_defaults[axis];
auto setting = new IntSetting(EXTENDED, WG, makeGrblName(axis, 170), makename(def->name, "StallGuard"), def->stallguard, -64, 63, checkStallguard);
setting->setAxis(axis);
axis_settings[axis]->stallguard = setting;
}
for (axis = N_AXIS - 1; axis >= 0; axis--) {
def = &axis_defaults[axis];
auto setting = new IntSetting(EXTENDED, WG, makeGrblName(axis, 160), makename(def->name, "Microsteps"), def->microsteps, 0, 256, checkMicrosteps);
setting->setAxis(axis);
axis_settings[axis]->microsteps = setting;
}
for (axis = N_AXIS - 1; axis >= 0; axis--) {
def = &axis_defaults[axis];
auto setting = new FloatSetting(EXTENDED, WG, makeGrblName(axis, 150), makename(def->name, "Current/Hold"), def->hold_current, 0.05, 20.0, checkHoldcurrent); // Amps
setting->setAxis(axis);
axis_settings[axis]->hold_current = setting;
}
for (axis = N_AXIS - 1; axis >= 0; axis--) {
def = &axis_defaults[axis];
auto setting = new FloatSetting(EXTENDED, WG, makeGrblName(axis, 140), makename(def->name, "Current/Run"), def->run_current, 0.0, 20.0, checkRunCurrent); // Amps
setting->setAxis(axis);
axis_settings[axis]->run_current = setting;
}
for (axis = N_AXIS - 1; axis >= 0; axis--) {
def = &axis_defaults[axis];
auto setting = new FloatSetting(GRBL, WG, makeGrblName(axis, 130), makename(def->name, "MaxTravel"), def->max_travel, 1.0, 100000.0);
setting->setAxis(axis);
axis_settings[axis]->max_travel = setting;
}
for (axis = N_AXIS - 1; axis >= 0; axis--) {
def = &axis_defaults[axis];
auto setting = new FloatSetting(GRBL, WG, makeGrblName(axis, 120), makename(def->name, "Acceleration"), def->acceleration, 1.0, 100000.0);
setting->setAxis(axis);
axis_settings[axis]->acceleration = setting;
}
for (axis = N_AXIS - 1; axis >= 0; axis--) {
def = &axis_defaults[axis];
auto setting = new FloatSetting(GRBL, WG, makeGrblName(axis, 110), makename(def->name, "MaxRate"), def->max_rate, 1.0, 100000.0);
setting->setAxis(axis);
axis_settings[axis]->max_rate = setting;
}
for (axis = N_AXIS - 1; axis >= 0; axis--) {
def = &axis_defaults[axis];
auto setting = new FloatSetting(GRBL, WG, makeGrblName(axis, 100), makename(def->name, "StepsPerMm"), def->steps_per_mm, 1.0, 100000.0);
setting->setAxis(axis);
axis_settings[axis]->steps_per_mm = setting;
}
// Spindle Settings
spindle_pwm_max_value = new FloatSetting(EXTENDED, WG, "36", "Spindle/PWM/Max", DEFAULT_SPINDLE_MAX_VALUE, 0.0, 100.0);
spindle_pwm_min_value = new FloatSetting(EXTENDED, WG, "35", "Spindle/PWM/Min", DEFAULT_SPINDLE_MIN_VALUE, 0.0, 100.0);
spindle_pwm_off_value = new FloatSetting(EXTENDED, WG, "34", "Spindle/PWM/Off", DEFAULT_SPINDLE_OFF_VALUE, 0.0, 100.0); // these are percentages
// IntSetting spindle_pwm_bit_precision(EXTENDED, WG, "Spindle/PWM/Precision", DEFAULT_SPINDLE_BIT_PRECISION, 1, 16);
spindle_pwm_freq = new FloatSetting(EXTENDED, WG, "33", "Spindle/PWM/Frequency", DEFAULT_SPINDLE_FREQ, 0, 100000);
// GRBL Non-numbered settings
startup_line_0 = new StringSetting(GRBL, WG, "N0", "GCode/Line0", "", checkStartupLine);
startup_line_1 = new StringSetting(GRBL, WG, "N1", "GCode/Line1", "", checkStartupLine);
// GRBL Numbered Settings
laser_mode = new FlagSetting(GRBL, WG, "32", "GCode/LaserMode", DEFAULT_LASER_MODE);
// TODO Settings - also need to call my_spindle->init();
rpm_min = new FloatSetting(GRBL, WG, "31", "GCode/MinS", DEFAULT_SPINDLE_RPM_MIN, 0, 100000);
rpm_max = new FloatSetting(GRBL, WG, "30", "GCode/MaxS", DEFAULT_SPINDLE_RPM_MAX, 0, 100000);
homing_pulloff = new FloatSetting(GRBL, WG, "27", "Homing/Pulloff", DEFAULT_HOMING_PULLOFF, 0, 1000);
homing_debounce = new FloatSetting(GRBL, WG, "26", "Homing/Debounce", DEFAULT_HOMING_DEBOUNCE_DELAY, 0, 10000);
homing_seek_rate = new FloatSetting(GRBL, WG, "25", "Homing/Seek", DEFAULT_HOMING_SEEK_RATE, 0, 10000);
homing_feed_rate = new FloatSetting(GRBL, WG, "24", "Homing/Feed", DEFAULT_HOMING_FEED_RATE, 0, 10000);
// TODO Settings - need to call st_generate_step_invert_masks()
homing_dir_mask = new AxisMaskSetting(GRBL, WG, "23", "Homing/DirInvert", DEFAULT_HOMING_DIR_MASK);
// TODO Settings - need to call limits_init();
homing_enable = new FlagSetting(GRBL, WG, "22", "Homing/Enable", DEFAULT_HOMING_ENABLE);
// TODO Settings - need to check for HOMING_ENABLE
hard_limits = new FlagSetting(GRBL, WG, "21", "Limits/Hard", DEFAULT_HARD_LIMIT_ENABLE);
soft_limits = new FlagSetting(GRBL, WG, "20", "Limits/Soft", DEFAULT_SOFT_LIMIT_ENABLE, NULL);
report_inches = new FlagSetting(GRBL, WG, "13", "Report/Inches", DEFAULT_REPORT_INCHES);
// TODO Settings - also need to clear, but not set, soft_limits
arc_tolerance = new FloatSetting(GRBL, WG, "12", "GCode/ArcTolerance", DEFAULT_ARC_TOLERANCE, 0, 1);
junction_deviation = new FloatSetting(GRBL, WG, "11", "GCode/JunctionDeviation", DEFAULT_JUNCTION_DEVIATION, 0, 10);
status_mask = new IntSetting(GRBL, WG, "10", "Report/Status", DEFAULT_STATUS_REPORT_MASK, 0, 2);
probe_invert = new FlagSetting(GRBL, WG, "6", "Probe/Invert", DEFAULT_INVERT_PROBE_PIN);
limit_invert = new FlagSetting(GRBL, WG, "5", "Limits/Invert", DEFAULT_INVERT_LIMIT_PINS);
step_enable_invert = new FlagSetting(GRBL, WG, "4", "Stepper/EnableInvert", DEFAULT_INVERT_ST_ENABLE);
dir_invert_mask = new AxisMaskSetting(GRBL, WG, "3", "Stepper/DirInvert", DEFAULT_DIRECTION_INVERT_MASK);
step_invert_mask = new AxisMaskSetting(GRBL, WG, "2", "Stepper/StepInvert", DEFAULT_STEPPING_INVERT_MASK);
stepper_idle_lock_time = new IntSetting(GRBL, WG, "1", "Stepper/IdleTime", DEFAULT_STEPPER_IDLE_LOCK_TIME, 0, 255);
pulse_microseconds = new IntSetting(GRBL, WG, "0", "Stepper/Pulse", DEFAULT_STEP_PULSE_MICROSECONDS, 3, 1000);
spindle_type = new EnumSetting(NULL, EXTENDED, WG, NULL, "Spindle/Type", SPINDLE_TYPE_NONE, &spindleTypes);
stallguard_debug_mask = new AxisMaskSetting(EXTENDED, WG, NULL, "Report/StallGuard", 0, checkStallguardDebugMask);
}

View File

@@ -0,0 +1,51 @@
#pragma once
extern bool motorSettingChanged;
extern AxisSettings* x_axis_settings;
extern AxisSettings* y_axis_settings;
extern AxisSettings* z_axis_settings;
extern AxisSettings* a_axis_settings;
extern AxisSettings* b_axis_settings;
extern AxisSettings* c_axis_settings;
extern AxisSettings* axis_settings[];
extern StringSetting* startup_line_0;
extern StringSetting* startup_line_1;
extern StringSetting* build_info;
extern IntSetting* pulse_microseconds;
extern IntSetting* stepper_idle_lock_time;
extern AxisMaskSetting* step_invert_mask;
extern AxisMaskSetting* dir_invert_mask;
extern AxisMaskSetting* homing_dir_mask;
extern FlagSetting* step_enable_invert;
extern FlagSetting* limit_invert;
extern FlagSetting* probe_invert;
extern FlagSetting* report_inches;
extern FlagSetting* soft_limits;
extern FlagSetting* hard_limits;
extern FlagSetting* homing_enable;
extern FlagSetting* laser_mode;
extern IntSetting* status_mask;
extern FloatSetting* junction_deviation;
extern FloatSetting* arc_tolerance;
extern FloatSetting* homing_feed_rate;
extern FloatSetting* homing_seek_rate;
extern FloatSetting* homing_debounce;
extern FloatSetting* homing_pulloff;
extern FloatSetting* spindle_pwm_freq;
extern FloatSetting* rpm_max;
extern FloatSetting* rpm_min;
extern FloatSetting* spindle_pwm_off_value;
extern FloatSetting* spindle_pwm_min_value;
extern FloatSetting* spindle_pwm_max_value;
extern IntSetting* spindle_pwm_bit_precision;
extern EnumSetting* spindle_type;
extern AxisMaskSetting* stallguard_debug_mask;

View File

@@ -0,0 +1,167 @@
/*
10vSpindle.cpp
This is basically a PWM spindle with some changes, so a forward and
reverse signal can be sent.
The direction pins will act as enables for the 2 directions. There is usually
a min RPM with VFDs, that speed will remain even if speed is 0. You
must turn off both direction pins when enable is off.
Part of Grbl_ESP32
2020 - Bart Dring
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SpindleClass.h"
void _10vSpindle :: init() {
get_pins_and_settings(); // these gets the standard PWM settings, but many need to be changed for BESC
// a couple more pins not inherited from PWM Spindle
#ifdef SPINDLE_FORWARD_PIN
_forward_pin = SPINDLE_FORWARD_PIN;
#else
_forward_pin = UNDEFINED_PIN;
#endif
#ifdef SPINDLE_REVERSE_PIN
_reverse_pin = SPINDLE_REVERSE_PIN;
#else
_reverse_pin = UNDEFINED_PIN;
#endif
if (_output_pin == UNDEFINED_PIN) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning: BESC output pin not defined");
return; // We cannot continue without the output pin
}
ledcSetup(_spindle_pwm_chan_num, (double)_pwm_freq, _pwm_precision); // setup the channel
ledcAttachPin(_output_pin, _spindle_pwm_chan_num); // attach the PWM to the pin
pinMode(_enable_pin, OUTPUT);
pinMode(_direction_pin, OUTPUT);
pinMode(_forward_pin, OUTPUT);
pinMode(_reverse_pin, OUTPUT);
set_rpm(0);
config_message();
is_reversable = true; // these VFDs are always reversable
}
// prints the startup message of the spindle config
void _10vSpindle :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO,
"0-10V spindle Out:%s Enbl:%s, Dir:%s, Fwd:%s, Rev:%s, Freq:%dHz Res:%dbits",
pinName(_output_pin).c_str(),
pinName(_enable_pin).c_str(),
pinName(_direction_pin).c_str(),
pinName(_forward_pin).c_str(),
pinName(_reverse_pin).c_str(),
_pwm_freq,
_pwm_precision);
}
uint32_t _10vSpindle::set_rpm(uint32_t rpm) {
uint32_t pwm_value;
if (_output_pin == UNDEFINED_PIN)
return rpm;
// apply speed overrides
rpm = rpm * sys.spindle_speed_ovr / 100; // Scale by spindle speed override value (percent)
// apply limits limits
if ((_min_rpm >= _max_rpm) || (rpm >= _max_rpm))
rpm = _max_rpm;
else if (rpm != 0 && rpm <= _min_rpm)
rpm = _min_rpm;
sys.spindle_speed = rpm;
// determine the pwm value
if (rpm == 0) {
pwm_value = _pwm_off_value;
} else {
pwm_value = map_uint32_t(rpm, _min_rpm, _max_rpm, _pwm_min_value, _pwm_max_value);
}
set_output(pwm_value);
return rpm;
}
void _10vSpindle::set_state(uint8_t state, uint32_t rpm) {
if (sys.abort)
return; // Block during abort.
if (state == SPINDLE_DISABLE) { // Halt or set spindle direction and rpm.
sys.spindle_speed = 0;
stop();
} else {
set_spindle_dir_pin(state == SPINDLE_ENABLE_CW);
set_rpm(rpm);
}
set_enable_pin(state != SPINDLE_DISABLE);
sys.report_ovr_counter = 0; // Set to report change immediately
}
uint8_t _10vSpindle::get_state() {
if (_current_pwm_duty == 0 || _output_pin == UNDEFINED_PIN)
return (SPINDLE_STATE_DISABLE);
if (_direction_pin != UNDEFINED_PIN)
return digitalRead(_direction_pin) ? SPINDLE_STATE_CW : SPINDLE_STATE_CCW;
return (SPINDLE_STATE_CW);
}
void _10vSpindle::stop() {
// inverts are delt with in methods
set_enable_pin(false);
set_output(_pwm_off_value);
}
void _10vSpindle::set_enable_pin(bool enable) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "_10vSpindle::set_enable_pin");
if (_off_with_zero_speed && sys.spindle_speed == 0)
enable = false;
#ifdef INVERT_SPINDLE_ENABLE_PIN
enable = !enable;
#endif
digitalWrite(_enable_pin, enable);
// turn off anything that acts like an enable
if (!enable) {
digitalWrite(_direction_pin, enable);
digitalWrite(_forward_pin, enable);
digitalWrite(_reverse_pin, enable);
}
}
void _10vSpindle::set_spindle_dir_pin(bool Clockwise) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "_10vSpindle::set_spindle_dir_pin");
digitalWrite(_direction_pin, Clockwise);
digitalWrite(_forward_pin, Clockwise);
digitalWrite(_reverse_pin, ! Clockwise);
}

View File

@@ -73,8 +73,7 @@ void BESCSpindle :: init() {
ledcSetup(_spindle_pwm_chan_num, (double)_pwm_freq, _pwm_precision); // setup the channel ledcSetup(_spindle_pwm_chan_num, (double)_pwm_freq, _pwm_precision); // setup the channel
ledcAttachPin(_output_pin, _spindle_pwm_chan_num); // attach the PWM to the pin ledcAttachPin(_output_pin, _spindle_pwm_chan_num); // attach the PWM to the pin
if (_enable_pin != UNDEFINED_PIN) pinMode(_enable_pin, OUTPUT);
pinMode(_enable_pin, OUTPUT);
set_rpm(0); set_rpm(0);
@@ -85,12 +84,12 @@ void BESCSpindle :: init() {
void BESCSpindle :: config_message() { void BESCSpindle :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL, grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO, MSG_LEVEL_INFO,
"BESC spindle on Pin:%d Min:%0.2fms Max:%0.2fms Freq:%dHz Res:%dbits", "BESC spindle on Pin:%s Min:%0.2fms Max:%0.2fms Freq:%dHz Res:%dbits",
report_pin_number(_output_pin), pinName(_output_pin).c_str(),
BESC_MIN_PULSE_SECS * 1000.0, // convert to milliseconds BESC_MIN_PULSE_SECS * 1000.0, // convert to milliseconds
BESC_MAX_PULSE_SECS * 1000.0, // convert to milliseconds BESC_MAX_PULSE_SECS * 1000.0, // convert to milliseconds
report_pin_number(_pwm_freq), _pwm_freq,
report_pin_number(_pwm_precision)); _pwm_precision);
} }
uint32_t BESCSpindle::set_rpm(uint32_t rpm) { uint32_t BESCSpindle::set_rpm(uint32_t rpm) {
@@ -108,7 +107,7 @@ uint32_t BESCSpindle::set_rpm(uint32_t rpm) {
else if (rpm != 0 && rpm <= _min_rpm) else if (rpm != 0 && rpm <= _min_rpm)
rpm = _min_rpm; rpm = _min_rpm;
sys.spindle_speed = rpm; sys.spindle_speed = rpm;
// determine the pwm value // determine the pwm value
if (rpm == 0) { if (rpm == 0) {
pwm_value = _pwm_off_value; pwm_value = _pwm_off_value;

View File

@@ -30,8 +30,8 @@ void DacSpindle :: init() {
if (_output_pin == UNDEFINED_PIN) if (_output_pin == UNDEFINED_PIN)
return; return;
_min_rpm = settings.rpm_min; _min_rpm = rpm_min->get();
_max_rpm = settings.rpm_max; _max_rpm = rpm_max->get();
_pwm_min_value = 0; // not actually PWM...DAC counts _pwm_min_value = 0; // not actually PWM...DAC counts
_pwm_max_value = 255; // not actually PWM...DAC counts _pwm_max_value = 255; // not actually PWM...DAC counts
_gpio_ok = true; _gpio_ok = true;
@@ -42,12 +42,8 @@ void DacSpindle :: init() {
return; return;
} }
if (_enable_pin != UNDEFINED_PIN) pinMode(_enable_pin, OUTPUT);
pinMode(_enable_pin, OUTPUT); pinMode(_direction_pin, OUTPUT);
if (_direction_pin != UNDEFINED_PIN) {
pinMode(_direction_pin, OUTPUT);
}
is_reversable = (_direction_pin != UNDEFINED_PIN); is_reversable = (_direction_pin != UNDEFINED_PIN);
@@ -57,17 +53,17 @@ void DacSpindle :: init() {
void DacSpindle :: config_message() { void DacSpindle :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL, grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO, MSG_LEVEL_INFO,
"DAC spindle Output:%d, Enbl:%d, Dir:%d, Res:8bits", "DAC spindle Output:%s, Enbl:%s, Dir:%s, Res:8bits",
report_pin_number(_output_pin), pinName(_output_pin).c_str(),
report_pin_number(_enable_pin), // 255 means pin not defined pinName(_enable_pin).c_str(),
report_pin_number(_direction_pin)); // 255 means pin not defined pinName(_direction_pin).c_str());
} }
uint32_t DacSpindle::set_rpm(uint32_t rpm) { uint32_t DacSpindle::set_rpm(uint32_t rpm) {
if (_output_pin == UNDEFINED_PIN) if (_output_pin == UNDEFINED_PIN)
return rpm; return rpm;
uint32_t pwm_value; uint32_t pwm_value;
// apply overrides and limits // apply overrides and limits
rpm = rpm * sys.spindle_speed_ovr / 100; // Scale by spindle speed override value (percent) rpm = rpm * sys.spindle_speed_ovr / 100; // Scale by spindle speed override value (percent)

View File

@@ -116,7 +116,7 @@ void HuanyangSpindle :: init() {
1, // priority 1, // priority
&vfd_cmdTaskHandle, &vfd_cmdTaskHandle,
0 // core 0 // core
); );
_task_running = true; _task_running = true;
} }
@@ -196,12 +196,12 @@ bool HuanyangSpindle :: get_pins_and_settings() {
} }
void HuanyangSpindle :: config_message() { void HuanyangSpindle :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL, grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO, MSG_LEVEL_INFO,
"Huanyang Spindle Tx:%d Rx:%d RTS:%d", "Huanyang Spindle Tx:%s Rx:%s RTS:%s",
report_pin_number(_txd_pin), pinName(_txd_pin).c_str(),
report_pin_number(_rxd_pin), pinName(_rxd_pin).c_str(),
report_pin_number(_rts_pin)); pinName(_rts_pin).c_str());
} }
/* /*
@@ -211,8 +211,6 @@ void HuanyangSpindle :: config_message() {
0x01 0x03 0x01 0x11 0x30 0x44 Start spindle counter-clockwise 0x01 0x03 0x01 0x11 0x30 0x44 Start spindle counter-clockwise
*/ */
void HuanyangSpindle :: set_state(uint8_t state, uint32_t rpm) { void HuanyangSpindle :: set_state(uint8_t state, uint32_t rpm) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "VFD Set state");
if (sys.abort) if (sys.abort)
return; // Block during abort. return; // Block during abort.

View File

@@ -31,8 +31,8 @@ bool Laser :: isRateAdjusted() {
void Laser :: config_message() { void Laser :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL, grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO, MSG_LEVEL_INFO,
"Laser spindle on GPIO:%d, Freq:%.2fHz, Res:%dbits Laser mode:$32=%d", "Laser spindle on Pin:%s, Freq:%.2fHz, Res:%dbits Laser mode:$32=%d",
report_pin_number(_output_pin), pinName(_output_pin).c_str(),
_pwm_freq, _pwm_freq,
_pwm_precision, _pwm_precision,
isRateAdjusted()); // the current mode isRateAdjusted()); // the current mode

View File

@@ -38,14 +38,17 @@ void PWMSpindle :: init() {
return; // We cannot continue without the output pin return; // We cannot continue without the output pin
} }
if (_output_pin >= I2S_OUT_PIN_BASE) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning: Spindle output pin %s cannot do PWM", pinName(_output_pin).c_str());
return;
}
ledcSetup(_spindle_pwm_chan_num, (double)_pwm_freq, _pwm_precision); // setup the channel ledcSetup(_spindle_pwm_chan_num, (double)_pwm_freq, _pwm_precision); // setup the channel
ledcAttachPin(_output_pin, _spindle_pwm_chan_num); // attach the PWM to the pin ledcAttachPin(_output_pin, _spindle_pwm_chan_num); // attach the PWM to the pin
if (_enable_pin != UNDEFINED_PIN) pinMode(_enable_pin, OUTPUT);
pinMode(_enable_pin, OUTPUT); pinMode(_direction_pin, OUTPUT);
if (_direction_pin != UNDEFINED_PIN)
pinMode(_direction_pin, OUTPUT);
config_message(); config_message();
} }
@@ -60,7 +63,7 @@ void PWMSpindle :: get_pins_and_settings() {
_output_pin = UNDEFINED_PIN; _output_pin = UNDEFINED_PIN;
#endif #endif
#ifdef INVERT_SPINDLE_ENABLE_PIN #ifdef INVERT_SPINDLE_OUTPUT_PIN
_invert_pwm = true; _invert_pwm = true;
#else #else
_invert_pwm = false; _invert_pwm = false;
@@ -85,25 +88,25 @@ void PWMSpindle :: get_pins_and_settings() {
is_reversable = (_direction_pin != UNDEFINED_PIN); is_reversable = (_direction_pin != UNDEFINED_PIN);
_pwm_freq = (uint32_t)settings.spindle_pwm_freq; _pwm_freq = spindle_pwm_freq->get();
_pwm_precision = calc_pwm_precision(_pwm_freq); // detewrmine the best precision _pwm_precision = calc_pwm_precision(_pwm_freq); // detewrmine the best precision
_pwm_period = (1 << _pwm_precision); _pwm_period = (1 << _pwm_precision);
if (settings.spindle_pwm_min_value > settings.spindle_pwm_min_value) if (spindle_pwm_min_value->get() > spindle_pwm_min_value->get())
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning: Spindle min pwm is greater than max. Check $35 and $36"); grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Warning: Spindle min pwm is greater than max. Check $35 and $36");
// pre-caculate some PWM count values // pre-caculate some PWM count values
_pwm_off_value = (_pwm_period * (uint32_t)settings.spindle_pwm_off_value / 100.0); _pwm_off_value = (_pwm_period * spindle_pwm_off_value->get() / 100.0);
_pwm_min_value = (_pwm_period * (uint32_t)settings.spindle_pwm_min_value / 100.0); _pwm_min_value = (_pwm_period * spindle_pwm_min_value->get() / 100.0);
_pwm_max_value = (_pwm_period * (uint32_t)settings.spindle_pwm_max_value / 100.0); _pwm_max_value = (_pwm_period * spindle_pwm_max_value->get() / 100.0);
#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE #ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE
_min_rpm = RPM_MIN; _min_rpm = RPM_MIN;
_max_rpm = RPM_MAX; _max_rpm = RPM_MAX;
_piecewide_linear = true; _piecewide_linear = true;
#else #else
_min_rpm = (uint32_t)settings.rpm_min; _min_rpm = rpm_min->get();
_max_rpm = (uint32_t)settings.rpm_max; _max_rpm = rpm_max->get();
_piecewide_linear = false; _piecewide_linear = false;
#endif #endif
// The pwm_gradient is the pwm duty cycle units per rpm // The pwm_gradient is the pwm duty cycle units per rpm
@@ -168,19 +171,11 @@ void PWMSpindle::set_state(uint8_t state, uint32_t rpm) {
} }
uint8_t PWMSpindle::get_state() { uint8_t PWMSpindle::get_state() {
if (_current_pwm_duty == 0 || _output_pin == UNDEFINED_PIN) if (_current_pwm_duty == 0 || _output_pin == UNDEFINED_PIN)
return (SPINDLE_STATE_DISABLE); return (SPINDLE_STATE_DISABLE);
else { if (_direction_pin != UNDEFINED_PIN)
if (_direction_pin != UNDEFINED_PIN) { return digitalRead(_direction_pin) ? SPINDLE_STATE_CW : SPINDLE_STATE_CCW;
if (digitalRead(_direction_pin)) return (SPINDLE_STATE_CW);
return (SPINDLE_STATE_CW);
else
return (SPINDLE_STATE_CCW);
} else
return (SPINDLE_STATE_CW);
}
} }
void PWMSpindle::stop() { void PWMSpindle::stop() {
@@ -193,10 +188,10 @@ void PWMSpindle::stop() {
void PWMSpindle :: config_message() { void PWMSpindle :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL, grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO, MSG_LEVEL_INFO,
"PWM spindle Output:%d, Enbl:%d, Dir:%d, Freq:%dHz, Res:%dbits", "PWM spindle Output:%s, Enbl:%s, Dir:%s, Freq:%dHz, Res:%dbits",
report_pin_number(_output_pin), pinName(_output_pin).c_str(),
report_pin_number(_enable_pin), // 255 means pin not defined pinName(_enable_pin).c_str(),
report_pin_number(_direction_pin), // 255 means pin not defined pinName(_direction_pin).c_str(),
_pwm_freq, _pwm_freq,
_pwm_precision); _pwm_precision);
} }
@@ -214,7 +209,7 @@ void PWMSpindle::set_output(uint32_t duty) {
_current_pwm_duty = duty; _current_pwm_duty = duty;
if (_invert_pwm) if (_invert_pwm)
duty = (1 << _pwm_precision) - duty; duty = (1 << _pwm_precision) - duty;
ledcWrite(_spindle_pwm_chan_num, duty); ledcWrite(_spindle_pwm_chan_num, duty);
@@ -222,6 +217,7 @@ void PWMSpindle::set_output(uint32_t duty) {
} }
void PWMSpindle::set_enable_pin(bool enable) { void PWMSpindle::set_enable_pin(bool enable) {
if (_enable_pin == UNDEFINED_PIN) if (_enable_pin == UNDEFINED_PIN)
return; return;
@@ -233,11 +229,11 @@ void PWMSpindle::set_enable_pin(bool enable) {
#else #else
digitalWrite(_enable_pin, !enable); digitalWrite(_enable_pin, !enable);
#endif #endif
digitalWrite(_enable_pin, enable);
} }
void PWMSpindle::set_spindle_dir_pin(bool Clockwise) { void PWMSpindle::set_spindle_dir_pin(bool Clockwise) {
if (_direction_pin != UNDEFINED_PIN) digitalWrite(_direction_pin, Clockwise);
digitalWrite(_direction_pin, Clockwise);
} }

View File

@@ -32,12 +32,8 @@ void RelaySpindle::init() {
return; return;
pinMode(_output_pin, OUTPUT); pinMode(_output_pin, OUTPUT);
pinMode(_enable_pin, OUTPUT);
if (_enable_pin != UNDEFINED_PIN) pinMode(_direction_pin, OUTPUT);
pinMode(_enable_pin, OUTPUT);
if (_direction_pin != UNDEFINED_PIN)
pinMode(_direction_pin, OUTPUT);
is_reversable = (_direction_pin != UNDEFINED_PIN); is_reversable = (_direction_pin != UNDEFINED_PIN);
@@ -45,13 +41,13 @@ void RelaySpindle::init() {
} }
// prints the startup message of the spindle config // prints the startup message of the spindle config
void RelaySpindle :: config_message() { void RelaySpindle :: config_message() {
grbl_msg_sendf(CLIENT_SERIAL, grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO, MSG_LEVEL_INFO,
"Relay spindle Output:%d, Enbl:%d, Dir:%d", "Relay spindle Output:%s, Enbl:%s, Dir:%s",
report_pin_number(_output_pin), pinName(_output_pin).c_str(),
report_pin_number(_enable_pin), // 255 means pin not defined pinName(_enable_pin).c_str(),
report_pin_number(_direction_pin)); // 255 means pin not defined pinName(_direction_pin).c_str());
} }
uint32_t RelaySpindle::set_rpm(uint32_t rpm) { uint32_t RelaySpindle::set_rpm(uint32_t rpm) {
@@ -59,12 +55,8 @@ uint32_t RelaySpindle::set_rpm(uint32_t rpm) {
return rpm; return rpm;
sys.spindle_speed = rpm; sys.spindle_speed = rpm;
set_output(rpm != 0);
if (rpm == 0) {
set_output(0);
} else {
set_output(1);
}
return rpm; return rpm;
} }

View File

@@ -34,6 +34,7 @@
#include "Laser.cpp" #include "Laser.cpp"
#include "HuanyangSpindle.cpp" #include "HuanyangSpindle.cpp"
#include "BESCSpindle.cpp" #include "BESCSpindle.cpp"
#include "10vSpindle.cpp"
// An instance of each type of spindle is created here. // An instance of each type of spindle is created here.
@@ -45,11 +46,12 @@ Laser laser;
DacSpindle dac_spindle; DacSpindle dac_spindle;
HuanyangSpindle huanyang_spindle; HuanyangSpindle huanyang_spindle;
BESCSpindle besc_spindle; BESCSpindle besc_spindle;
_10vSpindle _10v_spindle;
void spindle_select(uint8_t spindle_type) { void spindle_select() {
switch (spindle_type) { switch (spindle_type->get()) {
case SPINDLE_TYPE_PWM: case SPINDLE_TYPE_PWM:
spindle = &pwm_spindle; spindle = &pwm_spindle;
break; break;
@@ -68,6 +70,9 @@ void spindle_select(uint8_t spindle_type) {
case SPINDLE_TYPE_BESC: case SPINDLE_TYPE_BESC:
spindle = &besc_spindle; spindle = &besc_spindle;
break; break;
case SPINDLE_TYPE_10V:
spindle = &_10v_spindle;
break;
case SPINDLE_TYPE_NONE: case SPINDLE_TYPE_NONE:
default: default:
spindle = &null_spindle; spindle = &null_spindle;

View File

@@ -36,6 +36,7 @@
#define SPINDLE_TYPE_DAC 4 #define SPINDLE_TYPE_DAC 4
#define SPINDLE_TYPE_HUANYANG 5 #define SPINDLE_TYPE_HUANYANG 5
#define SPINDLE_TYPE_BESC 6 #define SPINDLE_TYPE_BESC 6
#define SPINDLE_TYPE_10V 7
#ifndef SPINDLE_CLASS_H #ifndef SPINDLE_CLASS_H
#define SPINDLE_CLASS_H #define SPINDLE_CLASS_H
@@ -85,12 +86,11 @@ class PWMSpindle : public Spindle {
void stop(); void stop();
void config_message(); void config_message();
private: private:
int32_t _current_pwm_duty;
void set_spindle_dir_pin(bool Clockwise); void set_spindle_dir_pin(bool Clockwise);
protected: protected:
int32_t _current_pwm_duty;
uint32_t _min_rpm; uint32_t _min_rpm;
uint32_t _max_rpm; uint32_t _max_rpm;
uint32_t _pwm_off_value; uint32_t _pwm_off_value;
@@ -178,6 +178,22 @@ class BESCSpindle : public PWMSpindle {
uint32_t set_rpm(uint32_t rpm); uint32_t set_rpm(uint32_t rpm);
}; };
class _10vSpindle : public PWMSpindle {
public:
void init();
void config_message();
uint32_t set_rpm(uint32_t rpm);
uint8_t _forward_pin;
uint8_t _reverse_pin;
void set_state(uint8_t state, uint32_t rpm);
uint8_t get_state();
void stop();
protected:
void set_enable_pin(bool enable_pin);
void set_spindle_dir_pin(bool Clockwise);
};
extern Spindle* spindle; extern Spindle* spindle;
@@ -188,8 +204,9 @@ extern Laser laser;
extern DacSpindle dac_spindle; extern DacSpindle dac_spindle;
extern HuanyangSpindle huanyang_spindle; extern HuanyangSpindle huanyang_spindle;
extern BESCSpindle besc_spindle; extern BESCSpindle besc_spindle;
extern _10vSpindle _10v_spindle;
void spindle_select(uint8_t spindletype); void spindle_select();
// in HuanyangSpindle.cpp // in HuanyangSpindle.cpp
void vfd_cmd_task(void* pvParameters); void vfd_cmd_task(void* pvParameters);

1062
Grbl_Esp32/WebSettings.cpp Normal file

File diff suppressed because it is too large Load Diff

64
Grbl_Esp32/WebSettings.h Normal file
View File

@@ -0,0 +1,64 @@
/*
WebSettings.h - Definitions for WebUI-related settings.
Copyright (c) 2020 Mitch Bradley
Copyright (c) 2014 Luc Lebosse. All rights reserved.
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
extern StringSetting* wifi_sta_ssid;
extern StringSetting* wifi_sta_password;
#ifdef ENABLE_WIFI
extern EnumSetting* wifi_sta_mode;
extern IPaddrSetting* wifi_sta_ip;
extern IPaddrSetting* wifi_sta_gateway;
extern IPaddrSetting* wifi_sta_netmask;
extern StringSetting* wifi_ap_ssid;
extern StringSetting* wifi_ap_password;
extern IPaddrSetting* wifi_ap_ip;
extern IntSetting* wifi_ap_channel;
extern StringSetting* wifi_hostname;
extern EnumSetting* http_enable;
extern IntSetting* http_port;
extern EnumSetting* telnet_enable;
extern IntSetting* telnet_port;
#endif
#ifdef WIFI_OR_BLUETOOTH
extern EnumSetting* wifi_radio_mode;
#endif
#ifdef ENABLE_BLUETOOTH
extern StringSetting* bt_name;
#endif
#ifdef ENABLE_AUTHENTICATION
extern StringSetting* user_password;
extern StringSetting* admin_password;
#endif
#ifdef ENABLE_NOTIFICATIONS
extern EnumSetting* notification_type;
extern StringSetting* notification_t1;
extern StringSetting* notification_t2;
extern StringSetting* notification_ts;
#endif

View File

@@ -0,0 +1,41 @@
#include "grbl.h"
#ifdef ENABLE_AUTHENTICATION
// TODO Settings - need ADMIN_ONLY and if it is called without a parameter it sets the default
StringSetting* user_password;
StringSetting* admin_password;
void remove_password(char *str, auth_t& auth_level) {
String paramStr = String((const char*)str);
int pos = paramStr.indexOf("pwd=");
if (pos == -1) {
return;
}
// Truncate the str string at the pwd= .
// If the pwd= is preceded by a space, take off that space too.
int endpos = pos;
if (endpos && str[endpos-1] == ' ') {
--endpos;
}
str[endpos] = '\0';
// Upgrade the authentication level if a password
// for a higher level is present.
const char* password = str + pos + strlen("pwd=");
if (auth_level < LEVEL_USER) {
if (!strcmp(password, user_password->get())) {
auth_level = LEVEL_USER;
}
}
if (auth_level < LEVEL_ADMIN) {
if (!strcmp(password, admin_password->get())) {
auth_level = LEVEL_ADMIN;
}
}
}
#else
void remove_password(char *str, auth_t& auth_level) {
auth_level = LEVEL_ADMIN;
}
#endif

View File

@@ -0,0 +1,13 @@
#pragma once
//Authentication level
typedef enum {
LEVEL_GUEST = 0,
LEVEL_USER = 1,
LEVEL_ADMIN = 2
} auth_t;
#define MIN_LOCAL_PASSWORD_LENGTH 1
#define MAX_LOCAL_PASSWORD_LENGTH 16
void remove_password(char *str, auth_t& auth_level);

File diff suppressed because it is too large Load Diff

View File

@@ -22,34 +22,14 @@
#define COMMANDS_h #define COMMANDS_h
#include "config.h" #include "config.h"
//Authentication level
typedef enum {
LEVEL_GUEST = 0,
LEVEL_USER = 1,
LEVEL_ADMIN = 2
} level_authenticate_type;
// Define line flags. Includes comment type tracking and line overflow detection.
#define LINE_FLAG_OVERFLOW bit(0)
#define LINE_FLAG_COMMENT_PARENTHESES bit(1)
#define LINE_FLAG_COMMENT_SEMICOLON bit(2)
class ESPResponseStream; class ESPResponseStream;
class COMMANDS { class COMMANDS {
public: public:
static bool check_command(const char*, int* cmd, String& cmd_params);
static String get_param(String& cmd_params, const char* id, bool withspace);
static bool execute_internal_command(int cmd, String cmd_params, level_authenticate_type auth_level = LEVEL_GUEST, ESPResponseStream* espresponse = NULL);
static void wait(uint32_t milliseconds); static void wait(uint32_t milliseconds);
static void handle(); static void handle();
static void restart_ESP(); static void restart_ESP();
#ifdef ENABLE_AUTHENTICATION static bool isLocalPasswordValid(char* password);
static bool isadmin(String& cmd_params);
static bool isuser(String& cmd_params);
static bool isLocalPasswordValid(const char* password);
#endif
private : private :
static bool restart_ESP_module; static bool restart_ESP_module;
}; };

View File

@@ -62,13 +62,13 @@ Some features should not be changed. See notes below.
// NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y. // NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y.
// These homing cycle definitions precede the machine.h file so that the machine // These homing cycle definitions precede the machine.h file so that the machine
// definition can undefine them if necessary. // definition can undefine them if necessary.
#define HOMING_CYCLE_0 (1<<Z_AXIS) // TYPICALLY REQUIRED: First move Z to clear workspace. #define HOMING_CYCLE_0 bit(Z_AXIS) // TYPICALLY REQUIRED: First move Z to clear workspace.
#define HOMING_CYCLE_1 (1<<X_AXIS) #define HOMING_CYCLE_1 bit(X_AXIS)
#define HOMING_CYCLE_2 (1<<Y_AXIS) #define HOMING_CYCLE_2 bit(Y_AXIS)
// NOTE: The following is for for homing X and Y at the same time // NOTE: The following is for for homing X and Y at the same time
// #define HOMING_CYCLE_0 (1<<Z_AXIS) // first home z by itself // #define HOMING_CYCLE_0 bit(Z_AXIS) // first home z by itself
// #define HOMING_CYCLE_1 ((1<<X_AXIS)|(1<<Y_AXIS)) // Homes both X-Y in one cycle. NOT COMPATIBLE WITH COREXY!!! // #define HOMING_CYCLE_1 (bit(X_AXIS)|bit(Y_AXIS)) // Homes both X-Y in one cycle. NOT COMPATIBLE WITH COREXY!!!
// Inverts pin logic of the control command pins based on a mask. This essentially means you can use // Inverts pin logic of the control command pins based on a mask. This essentially means you can use
// normally-closed switches on the specified pins, rather than the default normally-open switches. // normally-closed switches on the specified pins, rather than the default normally-open switches.
@@ -88,6 +88,8 @@ Some features should not be changed. See notes below.
// machine_common.h contains settings that do not change // machine_common.h contains settings that do not change
#include "machine_common.h" #include "machine_common.h"
#define MAX_N_AXIS 6
// Number of axes defined (steppers, servos, etc) (valid range: 3 to 6) // Number of axes defined (steppers, servos, etc) (valid range: 3 to 6)
// Even if your machine only uses less than the minimum of 3, you should select 3 // Even if your machine only uses less than the minimum of 3, you should select 3
#ifndef N_AXIS #ifndef N_AXIS
@@ -116,6 +118,11 @@ Some features should not be changed. See notes below.
#define ENABLE_WIFI //enable wifi #define ENABLE_WIFI //enable wifi
#if defined(ENABLE_WIFI) || defined(ENABLE_BLUETOOTH)
#define WIFI_OR_BLUETOOTH
#endif
#define ENABLE_HTTP //enable HTTP and all related services #define ENABLE_HTTP //enable HTTP and all related services
#define ENABLE_OTA //enable OTA #define ENABLE_OTA //enable OTA
#define ENABLE_TELNET //enable telnet #define ENABLE_TELNET //enable telnet
@@ -127,22 +134,26 @@ Some features should not be changed. See notes below.
#define ENABLE_SERIAL2SOCKET_IN #define ENABLE_SERIAL2SOCKET_IN
#define ENABLE_SERIAL2SOCKET_OUT #define ENABLE_SERIAL2SOCKET_OUT
// Captive portal is used when WiFi is in access point mode. It lets the
// WebUI come up automatically in the browser, instead of requiring the user
// to browse manually to a default URL. It works like airport and hotel
// WiFi that takes you a special page as soon as you connect to that AP.
#define ENABLE_CAPTIVE_PORTAL #define ENABLE_CAPTIVE_PORTAL
// Warning! The current authentication implementation is too weak to provide
// security against an attacker, since passwords are stored and transmitted
// "in the clear" over unsecured channels. It should be treated as a
// "friendly suggestion" to prevent unwitting dangerous actions, rather than
// as effective security against malice.
//#define ENABLE_AUTHENTICATION //#define ENABLE_AUTHENTICATION
//CONFIGURE_EYECATCH_END (DO NOT MODIFY THIS LINE) //CONFIGURE_EYECATCH_END (DO NOT MODIFY THIS LINE)
#define NAMESPACE "GRBL" #define NAMESPACE "GRBL"
#define ESP_RADIO_MODE "RADIO_MODE"
#ifdef ENABLE_AUTHENTICATION #ifdef ENABLE_AUTHENTICATION
#define DEFAULT_ADMIN_PWD "admin" #define DEFAULT_ADMIN_PWD "admin"
#define DEFAULT_USER_PWD "user"; #define DEFAULT_USER_PWD "user"
#define DEFAULT_ADMIN_LOGIN "admin" #define DEFAULT_ADMIN_LOGIN "admin"
#define DEFAULT_USER_LOGIN "user" #define DEFAULT_USER_LOGIN "user"
#define ADMIN_PWD_ENTRY "ADMIN_PWD"
#define USER_PWD_ENTRY "USER_PWD"
#define AUTH_ENTRY_NB 20
#define MAX_LOCAL_PASSWORD_LENGTH 16
#define MIN_LOCAL_PASSWORD_LENGTH 1
#endif #endif
//Radio Mode //Radio Mode
@@ -291,7 +302,7 @@ Some features should not be changed. See notes below.
// Enable CoreXY kinematics. Use ONLY with CoreXY machines. // Enable CoreXY kinematics. Use ONLY with CoreXY machines.
// IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to // IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to
// #define HOMING_CYCLE_0 (1<<X_AXIS) and #define HOMING_CYCLE_1 (1<<Y_AXIS) // #define HOMING_CYCLE_0 bit(X_AXIS) and #define HOMING_CYCLE_1 bit(Y_AXIS)
// NOTE: This configuration option alters the motion of the X and Y axes to principle of operation // NOTE: This configuration option alters the motion of the X and Y axes to principle of operation
// defined at (http://corexy.com/theory.html). Motors are assumed to positioned and wired exactly as // defined at (http://corexy.com/theory.html). Motors are assumed to positioned and wired exactly as
// described, if not, motions may move in strange directions. Grbl requires the CoreXY A and B motors // described, if not, motions may move in strange directions. Grbl requires the CoreXY A and B motors
@@ -304,12 +315,15 @@ Some features should not be changed. See notes below.
// will be applied to all of them. This is useful when a user has a mixed set of limit pins with both // will be applied to all of them. This is useful when a user has a mixed set of limit pins with both
// normally-open(NO) and normally-closed(NC) switches installed on their machine. // normally-open(NO) and normally-closed(NC) switches installed on their machine.
// NOTE: PLEASE DO NOT USE THIS, unless you have a situation that needs it. // NOTE: PLEASE DO NOT USE THIS, unless you have a situation that needs it.
// #define INVERT_LIMIT_PIN_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)) // Default disabled. Uncomment to enable. // #define INVERT_LIMIT_PIN_MASK (bit(X_AXIS)|bit(Y_AXIS)) // Default disabled. Uncomment to enable.
// Inverts the spindle enable pin from low-disabled/high-enabled to low-enabled/high-disabled. Useful // Inverts the spindle enable pin from low-disabled/high-enabled to low-enabled/high-disabled. Useful
// for some pre-built electronic boards. // for some pre-built electronic boards.
// #define INVERT_SPINDLE_ENABLE_PIN // Default disabled. Uncomment to enable. // #define INVERT_SPINDLE_ENABLE_PIN // Default disabled. Uncomment to enable.
// Inverts the spindle PWM output pin from low-disabled/high-enabled to low-enabled/high-disabled.
// #define INVERT_SPINDLE_OUTPUT_PIN // Default disabled. Uncomment to enable.
// Inverts the selected coolant pin from low-disabled/high-enabled to low-enabled/high-disabled. Useful // Inverts the selected coolant pin from low-disabled/high-enabled to low-enabled/high-disabled. Useful
// for some pre-built electronic boards. // for some pre-built electronic boards.
// #define INVERT_COOLANT_FLOOD_PIN // Default disabled. Uncomment to enable. // #define INVERT_COOLANT_FLOOD_PIN // Default disabled. Uncomment to enable.
@@ -443,11 +457,17 @@ Some features should not be changed. See notes below.
// removed, capitalized letters, no comments) and is to be immediately executed by Grbl. Echoes will not be // removed, capitalized letters, no comments) and is to be immediately executed by Grbl. Echoes will not be
// sent upon a line buffer overflow, but should for all normal lines sent to Grbl. For example, if a user // sent upon a line buffer overflow, but should for all normal lines sent to Grbl. For example, if a user
// sendss the line 'g1 x1.032 y2.45 (test comment)', Grbl will echo back in the form '[echo: G1X1.032Y2.45]'. // sendss the line 'g1 x1.032 y2.45 (test comment)', Grbl will echo back in the form '[echo: G1X1.032Y2.45]'.
// Only GCode lines are echoed, not command lines starting with $ or [ESP.
// NOTE: Only use this for debugging purposes!! When echoing, this takes up valuable resources and can effect // NOTE: Only use this for debugging purposes!! When echoing, this takes up valuable resources and can effect
// performance. If absolutely needed for normal operation, the serial write buffer should be greatly increased // performance. If absolutely needed for normal operation, the serial write buffer should be greatly increased
// to help minimize transmission waiting within the serial write protocol. // to help minimize transmission waiting within the serial write protocol.
//#define REPORT_ECHO_LINE_RECEIVED // Default disabled. Uncomment to enable. //#define REPORT_ECHO_LINE_RECEIVED // Default disabled. Uncomment to enable.
// This is similar to REPORT_ECHO_LINE_RECEIVED and subject to all its caveats,
// but instead of echoing the pre-parsed line, it echos the raw line exactly as
// received, including not only GCode lines, but also $ and [ESP commands.
//#define REPORT_ECHO_RAW_LINE_RECEIVED // Default disabled. Uncomment to enable.
// Minimum planner junction speed. Sets the default minimum junction speed the planner plans to at // Minimum planner junction speed. Sets the default minimum junction speed the planner plans to at
// every buffer block junction, except for starting from rest and end of the buffer, which are always // every buffer block junction, except for starting from rest and end of the buffer, which are always
// zero. This value controls how fast the machine moves through junctions with no regard for acceleration // zero. This value controls how fast the machine moves through junctions with no regard for acceleration
@@ -486,7 +506,7 @@ Some features should not be changed. See notes below.
#define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) #define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds)
// For test use only. This uses the ESP32's RMT perifieral to generate step pulses // For test use only. This uses the ESP32's RMT peripheral to generate step pulses
// It allows the use of the STEP_PULSE_DELAY (see below) and it automatically ends the // It allows the use of the STEP_PULSE_DELAY (see below) and it automatically ends the
// pulse in one operation. // pulse in one operation.
// Dir Pin ____|-------------------- // Dir Pin ____|--------------------

View File

@@ -228,24 +228,25 @@
#endif #endif
// ============== Axis Acceleration ========= // ============== Axis Acceleration =========
#define SEC_PER_MIN_SQ (60.0*60.0) // Seconds Per Minute Squared, for acceleration conversion
// Default accelerations are expressed in mm/sec^2
#ifndef DEFAULT_X_ACCELERATION #ifndef DEFAULT_X_ACCELERATION
#define DEFAULT_X_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_ACCELERATION 200.0
#endif #endif
#ifndef DEFAULT_Y_ACCELERATION #ifndef DEFAULT_Y_ACCELERATION
#define DEFAULT_Y_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION 200.0
#endif #endif
#ifndef DEFAULT_Z_ACCELERATION #ifndef DEFAULT_Z_ACCELERATION
#define DEFAULT_Z_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Z_ACCELERATION 200.0
#endif #endif
#ifndef DEFAULT_A_ACCELERATION #ifndef DEFAULT_A_ACCELERATION
#define DEFAULT_A_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_A_ACCELERATION 200.0
#endif #endif
#ifndef DEFAULT_B_ACCELERATION #ifndef DEFAULT_B_ACCELERATION
#define DEFAULT_B_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_B_ACCELERATION 200.0
#endif #endif
#ifndef DEFAULT_C_ACCELERATION #ifndef DEFAULT_C_ACCELERATION
#define DEFAULT_C_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_C_ACCELERATION 200.0
#endif #endif
// ========= AXIS MAX TRAVEL ============ // ========= AXIS MAX TRAVEL ============
@@ -292,22 +293,22 @@
// ========== Motor hold current (SPI Drivers ) ============= // ========== Motor hold current (SPI Drivers ) =============
#ifndef DEFAULT_X_HOLD_CURRENT #ifndef DEFAULT_X_HOLD_CURRENT
#define DEFAULT_X_HOLD_CURRENT 50 // $150 percent of run current (extended set) #define DEFAULT_X_HOLD_CURRENT 0.125 // $150 current in amps (extended set)
#endif #endif
#ifndef DEFAULT_Y_HOLD_CURRENT #ifndef DEFAULT_Y_HOLD_CURRENT
#define DEFAULT_Y_HOLD_CURRENT 50 // $151 percent of run current (extended set) #define DEFAULT_Y_HOLD_CURRENT 0.125 // $151 current in amps (extended set)
#endif #endif
#ifndef DEFAULT_Z_HOLD_CURRENT #ifndef DEFAULT_Z_HOLD_CURRENT
#define DEFAULT_Z_HOLD_CURRENT 50 // $152 percent of run current (extended set) #define DEFAULT_Z_HOLD_CURRENT 0.125 // $152 current in amps (extended set)
#endif #endif
#ifndef DEFAULT_A_HOLD_CURRENT #ifndef DEFAULT_A_HOLD_CURRENT
#define DEFAULT_A_HOLD_CURRENT 50 // $153 percent of run current (extended set) #define DEFAULT_A_HOLD_CURRENT 0.125 // $153 current in amps (extended set)
#endif #endif
#ifndef DEFAULT_B_HOLD_CURRENT #ifndef DEFAULT_B_HOLD_CURRENT
#define DEFAULT_B_HOLD_CURRENT 50 // $154 percent of run current (extended set) #define DEFAULT_B_HOLD_CURRENT 0.125 // $154 current in amps (extended set)
#endif #endif
#ifndef DEFAULT_C_HOLD_CURRENT #ifndef DEFAULT_C_HOLD_CURRENT
#define DEFAULT_C_HOLD_CURRENT 50 // $154 percent of run current (extended set) #define DEFAULT_C_HOLD_CURRENT 0.125 // $154 current in amps (extended set)
#endif #endif
// ========== Microsteps (SPI Drivers ) ================ // ========== Microsteps (SPI Drivers ) ================
@@ -352,8 +353,72 @@
#define DEFAULT_C_STALLGUARD 16 // $175 stallguard (extended set) #define DEFAULT_C_STALLGUARD 16 // $175 stallguard (extended set)
#endif #endif
// ================== pin defaults ========================
// Here is a place to default pins to UNDEFINED_PIN.
// This can eliminate checking to see if the pin is defined because
// the overridden pinMode and digitalWrite functions will deal with it.
#ifndef STEPPERS_DISABLE_PIN
#define STEPPERS_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef X_DISABLE_PIN
#define X_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef Y_DISABLE_PIN
#define Y_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef Z_DISABLE_PIN
#define Z_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef A_DISABLE_PIN
#define A_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef B_DISABLE_PIN
#define B_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef C_DISABLE_PIN
#define C_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef X2_DISABLE_PIN
#define X2_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef Y2_DISABLE_PIN
#define Y2_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef Z2_DISABLE_PIN
#define Z2_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef A2_DISABLE_PIN
#define A2_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef B2_DISABLE_PIN
#define B2_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef C2_DISABLE_PIN
#define C2_DISABLE_PIN UNDEFINED_PIN
#endif
#ifndef X_LIMIT_PIN
#define X_LIMIT_PIN UNDEFINED_PIN
#endif
#ifndef Y_LIMIT_PIN
#define Y_LIMIT_PIN UNDEFINED_PIN
#endif
#ifndef Z_LIMIT_PIN
#define Z_LIMIT_PIN UNDEFINED_PIN
#endif
#ifndef A_LIMIT_PIN
#define A_LIMIT_PIN UNDEFINED_PIN
#endif
#ifndef B_LIMIT_PIN
#define B_LIMIT_PIN UNDEFINED_PIN
#endif
#ifndef C_LIMIT_PIN
#define C_LIMIT_PIN UNDEFINED_PIN
#endif
#endif #endif

View File

@@ -17,15 +17,13 @@
License along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "config.h" #include "grbl.h"
#include "espresponse.h" #include "espresponse.h"
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI) #if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
#include "web_server.h" #include "web_server.h"
#include <WebServer.h> #include <WebServer.h>
#endif #endif
#include "report.h"
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI) #if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
ESPResponseStream::ESPResponseStream(WebServer* webserver) { ESPResponseStream::ESPResponseStream(WebServer* webserver) {
_header_sent = false; _header_sent = false;

View File

@@ -20,7 +20,6 @@
#ifndef ESPRESPONSE_h #ifndef ESPRESPONSE_h
#define ESPRESPONSE_h #define ESPRESPONSE_h
#include "config.h"
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI) #if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
class WebServer; class WebServer;
@@ -31,6 +30,7 @@ class ESPResponseStream {
void print(const char* data); void print(const char* data);
void println(const char* data); void println(const char* data);
void flush(); void flush();
bool anyOutput() { return _header_sent; }
static String formatBytes(uint64_t bytes); static String formatBytes(uint64_t bytes);
uint8_t client() {return _client;} uint8_t client() {return _client;}
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI) #if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
@@ -40,8 +40,8 @@ class ESPResponseStream {
ESPResponseStream(); ESPResponseStream();
private: private:
uint8_t _client; uint8_t _client;
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
bool _header_sent; bool _header_sent;
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
WebServer* _webserver; WebServer* _webserver;
String _buffer; String _buffer;
#endif #endif

View File

@@ -57,12 +57,78 @@ void gc_sync_position() {
} }
// Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase // Edit GCode line in-place, removing whitespace and comments and
// characters and signed floating point values (no whitespace). Comments and block delete // converting to uppercase
// characters have been removed. In this function, all units and positions are converted and void collapseGCode(char *line) {
// parenPtr, if non-NULL, is the address of the character after (
char* parenPtr = NULL;
// outPtr is the address where newly-processed characters will be placed.
// outPtr is alway less than or equal to inPtr.
char* outPtr = line;
char c;
for (char* inPtr = line; (c = *inPtr) != '\0'; inPtr++) {
if (isspace(c)) {
continue;
}
switch (c) {
case ')':
if (parenPtr) {
// Terminate comment by replacing ) with NUL
*inPtr = '\0';
report_gcode_comment(parenPtr);
parenPtr = NULL;
}
// Strip out ) that does not follow a (
break;
case '(':
// Start the comment at the character after (
parenPtr = inPtr + 1;
break;
case ';':
// NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST.
#ifdef REPORT_SEMICOLON_COMMENTS
report_gcode_comment(inPtr + 1);
#endif
*outPtr = '\0';
return;
case '%':
// TODO: Install '%' feature
// Program start-end percent sign NOT SUPPORTED.
// NOTE: This maybe installed to tell Grbl when a program is running vs manual input,
// where, during a program, the system auto-cycle start will continue to execute
// everything until the next '%' sign. This will help fix resuming issues with certain
// functions that empty the planner buffer to execute its task on-time.
break;
case '\r':
// In case one sneaks in
break;
default:
if (!parenPtr) {
*outPtr++ = toupper(c); // make upper case
}
}
}
// On loop exit, *inPtr is '\0'
if (parenPtr) {
// Handle unterminated ( comments
report_gcode_comment(parenPtr);
}
*outPtr = '\0';
}
// Executes one line of NUL-terminated G-Code.
// The line may contain whitespace and comments, which are first removed,
// and lower case characters, which are converted to upper case.
// In this function, all units and positions are converted and
// exported to grbl's internal functions in terms of (mm, mm/min) and absolute machine // exported to grbl's internal functions in terms of (mm, mm/min) and absolute machine
// coordinates, respectively. // coordinates, respectively.
uint8_t gc_execute_line(char* line, uint8_t client) { uint8_t gc_execute_line(char* line, uint8_t client) {
// Step 0 - remove whitespace and comments and convert to upper case
collapseGCode(line);
#ifdef REPORT_ECHO_LINE_RECEIVED
report_echo_line_received(line, client);
#endif
/* ------------------------------------------------------------------------------------- /* -------------------------------------------------------------------------------------
STEP 1: Initialize parser block struct and copy current g-code state modes. The parser STEP 1: Initialize parser block struct and copy current g-code state modes. The parser
updates these modes and commands as the block line is parser and will only be used and updates these modes and commands as the block line is parser and will only be used and
@@ -301,7 +367,7 @@ uint8_t gc_execute_line(char* line, uint8_t client) {
gc_block.modal.spindle = SPINDLE_ENABLE_CW; gc_block.modal.spindle = SPINDLE_ENABLE_CW;
break; break;
case 4: // Supported if SPINDLE_DIR_PIN is defined or laser mode is on. case 4: // Supported if SPINDLE_DIR_PIN is defined or laser mode is on.
if (spindle->is_reversable || bit_istrue(settings.flags, BITFLAG_LASER_MODE)) if (spindle->is_reversable || laser_mode->get())
gc_block.modal.spindle = SPINDLE_ENABLE_CCW; gc_block.modal.spindle = SPINDLE_ENABLE_CCW;
else else
FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND);
@@ -378,21 +444,21 @@ uint8_t gc_execute_line(char* line, uint8_t client) {
case 'A': case 'A':
word_bit = WORD_A; word_bit = WORD_A;
gc_block.values.xyz[A_AXIS] = value; gc_block.values.xyz[A_AXIS] = value;
axis_words |= (1 << A_AXIS); axis_words |= bit(A_AXIS);
break; break;
#endif #endif
#if (N_AXIS > B_AXIS) #if (N_AXIS > B_AXIS)
case 'B': case 'B':
word_bit = WORD_B; word_bit = WORD_B;
gc_block.values.xyz[B_AXIS] = value; gc_block.values.xyz[B_AXIS] = value;
axis_words |= (1 << B_AXIS); axis_words |= bit(B_AXIS);
break; break;
#endif #endif
#if (N_AXIS > C_AXIS) #if (N_AXIS > C_AXIS)
case 'C': case 'C':
word_bit = WORD_C; word_bit = WORD_C;
gc_block.values.xyz[C_AXIS] = value; gc_block.values.xyz[C_AXIS] = value;
axis_words |= (1 << C_AXIS); axis_words |= bit(C_AXIS);
break; break;
#endif #endif
// case 'D': // Not supported // case 'D': // Not supported
@@ -404,17 +470,17 @@ uint8_t gc_execute_line(char* line, uint8_t client) {
case 'I': case 'I':
word_bit = WORD_I; word_bit = WORD_I;
gc_block.values.ijk[X_AXIS] = value; gc_block.values.ijk[X_AXIS] = value;
ijk_words |= (1 << X_AXIS); ijk_words |= bit(X_AXIS);
break; break;
case 'J': case 'J':
word_bit = WORD_J; word_bit = WORD_J;
gc_block.values.ijk[Y_AXIS] = value; gc_block.values.ijk[Y_AXIS] = value;
ijk_words |= (1 << Y_AXIS); ijk_words |= bit(Y_AXIS);
break; break;
case 'K': case 'K':
word_bit = WORD_K; word_bit = WORD_K;
gc_block.values.ijk[Z_AXIS] = value; gc_block.values.ijk[Z_AXIS] = value;
ijk_words |= (1 << Z_AXIS); ijk_words |= bit(Z_AXIS);
break; break;
case 'L': case 'L':
word_bit = WORD_L; word_bit = WORD_L;
@@ -448,17 +514,17 @@ uint8_t gc_execute_line(char* line, uint8_t client) {
case 'X': case 'X':
word_bit = WORD_X; word_bit = WORD_X;
gc_block.values.xyz[X_AXIS] = value; gc_block.values.xyz[X_AXIS] = value;
axis_words |= (1 << X_AXIS); axis_words |= bit(X_AXIS);
break; break;
case 'Y': case 'Y':
word_bit = WORD_Y; word_bit = WORD_Y;
gc_block.values.xyz[Y_AXIS] = value; gc_block.values.xyz[Y_AXIS] = value;
axis_words |= (1 << Y_AXIS); axis_words |= bit(Y_AXIS);
break; break;
case 'Z': case 'Z':
word_bit = WORD_Z; word_bit = WORD_Z;
gc_block.values.xyz[Z_AXIS] = value; gc_block.values.xyz[Z_AXIS] = value;
axis_words |= (1 << Z_AXIS); axis_words |= bit(Z_AXIS);
break; break;
default: default:
FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND);
@@ -635,7 +701,7 @@ uint8_t gc_execute_line(char* line, uint8_t client) {
// is absent or if any of the other axis words are present. // is absent or if any of the other axis words are present.
if (axis_command == AXIS_COMMAND_TOOL_LENGTH_OFFSET) { // Indicates called in block. if (axis_command == AXIS_COMMAND_TOOL_LENGTH_OFFSET) { // Indicates called in block.
if (gc_block.modal.tool_length == TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC) { if (gc_block.modal.tool_length == TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC) {
if (axis_words ^ (1 << TOOL_LENGTH_OFFSET_AXIS)) if (axis_words ^ bit(TOOL_LENGTH_OFFSET_AXIS))
FAIL(STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR); FAIL(STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR);
} }
} }
@@ -671,7 +737,7 @@ uint8_t gc_execute_line(char* line, uint8_t client) {
if (!axis_words) { if (!axis_words) {
FAIL(STATUS_GCODE_NO_AXIS_WORDS) FAIL(STATUS_GCODE_NO_AXIS_WORDS)
}; // [No axis words] }; // [No axis words]
if (bit_isfalse(value_words, ((1 << WORD_P) | (1 << WORD_L)))) { if (bit_isfalse(value_words, (bit(WORD_P) | bit(WORD_L)))) {
FAIL(STATUS_GCODE_VALUE_WORD_MISSING); // [P/L word missing] FAIL(STATUS_GCODE_VALUE_WORD_MISSING); // [P/L word missing]
} }
coord_select = trunc(gc_block.values.p); // Convert p value to int. coord_select = trunc(gc_block.values.p); // Convert p value to int.
@@ -776,7 +842,7 @@ uint8_t gc_execute_line(char* line, uint8_t client) {
if (axis_words) { if (axis_words) {
// Move only the axes specified in secondary move. // Move only the axes specified in secondary move.
for (idx = 0; idx < N_AXIS; idx++) { for (idx = 0; idx < N_AXIS; idx++) {
if (!(axis_words & (1 << idx))) if (!(axis_words & bit(idx)))
gc_block.values.ijk[idx] = gc_state.position[idx]; gc_block.values.ijk[idx] = gc_state.position[idx];
} }
} else { } else {
@@ -1037,7 +1103,7 @@ uint8_t gc_execute_line(char* line, uint8_t client) {
return (status); return (status);
} }
// If in laser mode, setup laser power based on current and past parser conditions. // If in laser mode, setup laser power based on current and past parser conditions.
if (bit_istrue(settings.flags, BITFLAG_LASER_MODE)) { if (laser_mode->get()) {
if (!((gc_block.modal.motion == MOTION_MODE_LINEAR) || (gc_block.modal.motion == MOTION_MODE_CW_ARC) if (!((gc_block.modal.motion == MOTION_MODE_LINEAR) || (gc_block.modal.motion == MOTION_MODE_CW_ARC)
|| (gc_block.modal.motion == MOTION_MODE_CCW_ARC))) || (gc_block.modal.motion == MOTION_MODE_CCW_ARC)))
gc_parser_flags |= GC_PARSER_LASER_DISABLE; gc_parser_flags |= GC_PARSER_LASER_DISABLE;
@@ -1125,7 +1191,7 @@ uint8_t gc_execute_line(char* line, uint8_t client) {
// turn on/off an i/o pin // turn on/off an i/o pin
if ((gc_block.modal.io_control == NON_MODAL_IO_ENABLE) || (gc_block.modal.io_control == NON_MODAL_IO_DISABLE)) { if ((gc_block.modal.io_control == NON_MODAL_IO_ENABLE) || (gc_block.modal.io_control == NON_MODAL_IO_DISABLE)) {
if (gc_block.values.p <= MAX_USER_DIGITAL_PIN) if (gc_block.values.p <= MAX_USER_DIGITAL_PIN)
sys_io_control(1 << (int)gc_block.values.p, (gc_block.modal.io_control == NON_MODAL_IO_ENABLE)); sys_io_control(bit((int)gc_block.values.p), (gc_block.modal.io_control == NON_MODAL_IO_ENABLE));
else else
FAIL(STATUS_P_PARAM_MAX_EXCEEDED); FAIL(STATUS_P_PARAM_MAX_EXCEEDED);
} }

View File

@@ -21,8 +21,8 @@
#pragma once #pragma once
// Grbl versioning system // Grbl versioning system
#define GRBL_VERSION "1.2a" #define GRBL_VERSION "1.3a"
#define GRBL_VERSION_BUILD "20200710" #define GRBL_VERSION_BUILD "20200720"
//#include <sdkconfig.h> //#include <sdkconfig.h>
#include <Arduino.h> #include <Arduino.h>
@@ -41,6 +41,7 @@
#include "defaults.h" #include "defaults.h"
#include "settings.h" #include "settings.h"
#include "authentication.h"
#include "system.h" #include "system.h"
#include "planner.h" #include "planner.h"
@@ -54,19 +55,24 @@
#include "protocol.h" #include "protocol.h"
#include "report.h" #include "report.h"
#include "serial.h" #include "serial.h"
#include "Pins.h"
#include "Spindles/SpindleClass.h" #include "Spindles/SpindleClass.h"
#include "Motors/MotorClass.h"
#include "stepper.h" #include "stepper.h"
#include "jog.h" #include "jog.h"
#include "inputbuffer.h" #include "inputbuffer.h"
#include "commands.h"
#include "SettingsClass.h"
#include "SettingsDefinitions.h"
#include "WebSettings.h"
// Do not guard this because it is needed for local files too
#include "grbl_sd.h"
#ifdef ENABLE_BLUETOOTH #ifdef ENABLE_BLUETOOTH
#include "BTconfig.h" #include "BTconfig.h"
#endif #endif
#ifdef ENABLE_SD_CARD
#include "grbl_sd.h"
#endif
#ifdef ENABLE_WIFI #ifdef ENABLE_WIFI
#include "wificonfig.h" #include "wificonfig.h"
#ifdef ENABLE_HTTP #ifdef ENABLE_HTTP
@@ -94,6 +100,10 @@
#include "grbl_unipolar.h" #include "grbl_unipolar.h"
#endif #endif
#ifdef USE_I2S_OUT
#include "i2s_out.h"
#endif
// Called if USE_MACHINE_INIT is defined // Called if USE_MACHINE_INIT is defined
void machine_init(); void machine_init();

View File

@@ -20,7 +20,7 @@
#include "grbl.h" #include "grbl.h"
void memcpy_to_eeprom_with_checksum(unsigned int destination, char* source, unsigned int size) { void memcpy_to_eeprom_with_checksum(unsigned int destination, const char* source, unsigned int size) {
unsigned char checksum = 0; unsigned char checksum = 0;
for (; size > 0; size--) { for (; size > 0; size--) {
checksum = (checksum << 1) || (checksum >> 7); checksum = (checksum << 1) || (checksum >> 7);

View File

@@ -25,7 +25,7 @@
//unsigned char eeprom_get_char(unsigned int addr); //unsigned char eeprom_get_char(unsigned int addr);
//void eeprom_put_char(unsigned int addr, unsigned char new_value); //void eeprom_put_char(unsigned int addr, unsigned char new_value);
void memcpy_to_eeprom_with_checksum(unsigned int destination, char* source, unsigned int size); void memcpy_to_eeprom_with_checksum(unsigned int destination, const char* source, unsigned int size);
int memcpy_from_eeprom_with_checksum(char* destination, unsigned int source, unsigned int size); int memcpy_from_eeprom_with_checksum(char* destination, unsigned int source, unsigned int size);
#endif #endif

View File

@@ -77,6 +77,7 @@ void IRAM_ATTR isr_limit_switches() {
void limits_go_home(uint8_t cycle_mask) { void limits_go_home(uint8_t cycle_mask) {
if (sys.abort) return; // Block if system reset has been issued. if (sys.abort) return; // Block if system reset has been issued.
// Initialize plan data struct for homing motion. Spindle and coolant are disabled. // Initialize plan data struct for homing motion. Spindle and coolant are disabled.
motors_set_homing_mode(cycle_mask, true); // tell motors homing is about to start
plan_line_data_t plan_data; plan_line_data_t plan_data;
plan_line_data_t* pl_data = &plan_data; plan_line_data_t* pl_data = &plan_data;
memset(pl_data, 0, sizeof(plan_line_data_t)); memset(pl_data, 0, sizeof(plan_line_data_t));
@@ -98,13 +99,12 @@ void limits_go_home(uint8_t cycle_mask) {
#endif #endif
if (bit_istrue(cycle_mask, bit(idx))) { if (bit_istrue(cycle_mask, bit(idx))) {
// Set target based on max_travel setting. Ensure homing switches engaged with search scalar. // Set target based on max_travel setting. Ensure homing switches engaged with search scalar.
// NOTE: settings.max_travel[] is stored as a negative value. max_travel = MAX(max_travel, (HOMING_AXIS_SEARCH_SCALAR) * axis_settings[idx]->max_travel->get());
max_travel = MAX(max_travel, (-HOMING_AXIS_SEARCH_SCALAR) * settings.max_travel[idx]);
} }
} }
// Set search mode with approach at seek rate to quickly engage the specified cycle_mask limit switches. // Set search mode with approach at seek rate to quickly engage the specified cycle_mask limit switches.
bool approach = true; bool approach = true;
float homing_rate = settings.homing_seek_rate; float homing_rate = homing_seek_rate->get();
uint8_t limit_state, axislock, n_active_axis; uint8_t limit_state, axislock, n_active_axis;
do { do {
system_convert_array_steps_to_mpos(target, sys_position); system_convert_array_steps_to_mpos(target, sys_position);
@@ -130,7 +130,8 @@ void limits_go_home(uint8_t cycle_mask) {
#endif #endif
// Set target direction based on cycle mask and homing cycle approach state. // Set target direction based on cycle mask and homing cycle approach state.
// NOTE: This happens to compile smaller than any other implementation tried. // NOTE: This happens to compile smaller than any other implementation tried.
if (bit_istrue(settings.homing_dir_mask, bit(idx))) { auto mask = homing_dir_mask->get();
if (bit_istrue(mask, bit(idx))) {
if (approach) target[idx] = -max_travel; if (approach) target[idx] = -max_travel;
else target[idx] = max_travel; else target[idx] = max_travel;
} else { } else {
@@ -155,7 +156,7 @@ void limits_go_home(uint8_t cycle_mask) {
limit_state = limits_get_state(); limit_state = limits_get_state();
for (idx = 0; idx < N_AXIS; idx++) { for (idx = 0; idx < N_AXIS; idx++) {
if (axislock & step_pin[idx]) { if (axislock & step_pin[idx]) {
if (limit_state & (1 << idx)) { if (limit_state & bit(idx)) {
#ifdef COREXY #ifdef COREXY
if (idx == Z_AXIS) axislock &= ~(step_pin[Z_AXIS]); if (idx == Z_AXIS) axislock &= ~(step_pin[Z_AXIS]);
else axislock &= ~(step_pin[A_MOTOR] | step_pin[B_MOTOR]); else axislock &= ~(step_pin[A_MOTOR] | step_pin[B_MOTOR]);
@@ -179,7 +180,8 @@ void limits_go_home(uint8_t cycle_mask) {
if (!approach && (limits_get_state() & cycle_mask)) system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF); if (!approach && (limits_get_state() & cycle_mask)) system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF);
// Homing failure condition: Limit switch not found during approach. // Homing failure condition: Limit switch not found during approach.
if (approach && (rt_exec & EXEC_CYCLE_STOP)) system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH); if (approach && (rt_exec & EXEC_CYCLE_STOP)) system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH);
if (sys_rt_exec_alarm) { if (sys_rt_exec_alarm) {
motors_set_homing_mode(cycle_mask, false); // tell motors homing is done...failed
mc_reset(); // Stop motors, if they are running. mc_reset(); // Stop motors, if they are running.
protocol_execute_realtime(); protocol_execute_realtime();
return; return;
@@ -190,17 +192,22 @@ void limits_go_home(uint8_t cycle_mask) {
} }
} }
} while (STEP_MASK & axislock); } while (STEP_MASK & axislock);
#ifdef USE_I2S_OUT_STREAM
if (!approach) {
delay_ms(I2S_OUT_DELAY_MS);
}
#endif
st_reset(); // Immediately force kill steppers and reset step segment buffer. st_reset(); // Immediately force kill steppers and reset step segment buffer.
delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate. delay_ms(homing_debounce->get()); // Delay to allow transient dynamics to dissipate.
// Reverse direction and reset homing rate for locate cycle(s). // Reverse direction and reset homing rate for locate cycle(s).
approach = !approach; approach = !approach;
// After first cycle, homing enters locating phase. Shorten search to pull-off distance. // After first cycle, homing enters locating phase. Shorten search to pull-off distance.
if (approach) { if (approach) {
max_travel = settings.homing_pulloff * HOMING_AXIS_LOCATE_SCALAR; max_travel = homing_pulloff->get() * HOMING_AXIS_LOCATE_SCALAR;
homing_rate = settings.homing_feed_rate; homing_rate = homing_feed_rate->get();
} else { } else {
max_travel = settings.homing_pulloff; max_travel = homing_pulloff->get();
homing_rate = settings.homing_seek_rate; homing_rate = homing_seek_rate->get();
} }
} while (n_cycle-- > 0); } while (n_cycle-- > 0);
// The active cycle axes should now be homed and machine limits have been located. By // The active cycle axes should now be homed and machine limits have been located. By
@@ -211,23 +218,26 @@ void limits_go_home(uint8_t cycle_mask) {
// triggering when hard limits are enabled or when more than one axes shares a limit pin. // triggering when hard limits are enabled or when more than one axes shares a limit pin.
int32_t set_axis_position; int32_t set_axis_position;
// Set machine positions for homed limit switches. Don't update non-homed axes. // Set machine positions for homed limit switches. Don't update non-homed axes.
auto mask = homing_dir_mask->get();
auto pulloff = homing_pulloff->get();
for (idx = 0; idx < N_AXIS; idx++) { for (idx = 0; idx < N_AXIS; idx++) {
// NOTE: settings.max_travel[] is stored as a negative value. auto steps = axis_settings[idx]->steps_per_mm->get();
if (cycle_mask & bit(idx)) { if (cycle_mask & bit(idx)) {
#ifdef HOMING_FORCE_SET_ORIGIN #ifdef HOMING_FORCE_SET_ORIGIN
set_axis_position = 0; set_axis_position = 0;
#else #else
if (bit_istrue(settings.homing_dir_mask, bit(idx))) { auto travel = axis_settings[idx]->max_travel->get();
if (bit_istrue(mask, bit(idx))) {
#ifdef HOMING_FORCE_POSITIVE_SPACE #ifdef HOMING_FORCE_POSITIVE_SPACE
set_axis_position = 0; //lround(settings.homing_pulloff*settings.steps_per_mm[idx]); set_axis_position = 0; //lround(settings.homing_pulloff*settings.steps_per_mm[idx]);
#else #else
set_axis_position = lround((settings.max_travel[idx] + settings.homing_pulloff) * settings.steps_per_mm[idx]); set_axis_position = lround((-travel + pulloff) * steps);
#endif #endif
} else { } else {
#ifdef HOMING_FORCE_POSITIVE_SPACE #ifdef HOMING_FORCE_POSITIVE_SPACE
set_axis_position = lround((-settings.max_travel[idx] - settings.homing_pulloff) * settings.steps_per_mm[idx]); set_axis_position = lround((-travel - pulloff) * steps);
#else #else
set_axis_position = lround(-settings.homing_pulloff * settings.steps_per_mm[idx]); set_axis_position = lround(-pulloff * steps);
#endif #endif
} }
#endif #endif
@@ -248,71 +258,39 @@ void limits_go_home(uint8_t cycle_mask) {
} }
} }
sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation. sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation.
motors_set_homing_mode(cycle_mask, false); // tell motors homing is done
} }
uint8_t limit_pins[] = {
X_LIMIT_PIN,
Y_LIMIT_PIN,
Z_LIMIT_PIN,
A_LIMIT_PIN,
B_LIMIT_PIN,
C_LIMIT_PIN,
};
uint8_t limit_mask = 0;
void limits_init() { void limits_init() {
#ifndef DISABLE_LIMIT_PIN_PULL_UP limit_mask = 0;
#ifdef X_LIMIT_PIN int mode = INPUT_PULLUP;
pinMode(X_LIMIT_PIN, INPUT_PULLUP); // input with pullup #ifdef DISABLE_LIMIT_PIN_PULL_UP
mode = INPUT;
#endif #endif
#ifdef Y_LIMIT_PIN for (int i=0; i<N_AXIS; i++) {
pinMode(Y_LIMIT_PIN, INPUT_PULLUP); uint8_t pin;
#endif if ((pin = limit_pins[i]) != UNDEFINED_PIN) {
#ifdef Z_LIMIT_PIN limit_mask |= bit(i);
pinMode(Z_LIMIT_PIN, INPUT_PULLUP); pinMode(pin, mode);
#endif if (hard_limits->get()) {
#ifdef A_LIMIT_PIN attachInterrupt(pin, isr_limit_switches, CHANGE);
pinMode(A_LIMIT_PIN, INPUT_PULLUP); } else {
#endif detachInterrupt(pin);
#ifdef B_LIMIT_PIN }
pinMode(B_LIMIT_PIN, INPUT_PULLUP); }
#endif }
#ifdef C_LIMIT_PIN
pinMode(C_LIMIT_PIN, INPUT_PULLUP);
#endif
#else
#ifdef X_LIMIT_PIN
pinMode(X_LIMIT_PIN, INPUT); // input no pullup
#endif
#ifdef Y_LIMIT_PIN
pinMode(Y_LIMIT_PIN, INPUT);
#endif
#ifdef Z_LIMIT_PIN
pinMode(Z_LIMIT_PIN, INPUT);
#endif
#ifdef A_LIMIT_PIN
pinMode(A_LIMIT_PIN, INPUT); // input no pullup
#endif
#ifdef B_LIMIT_PIN
pinMode(B_LIMIT_PIN, INPUT);
#endif
#ifdef C_LIMIT_PIN
pinMode(C_LIMIT_PIN, INPUT);
#endif
#endif
if (bit_istrue(settings.flags, BITFLAG_HARD_LIMIT_ENABLE)) {
// attach interrupt to them
#ifdef X_LIMIT_PIN
attachInterrupt(digitalPinToInterrupt(X_LIMIT_PIN), isr_limit_switches, CHANGE);
#endif
#ifdef Y_LIMIT_PIN
attachInterrupt(digitalPinToInterrupt(Y_LIMIT_PIN), isr_limit_switches, CHANGE);
#endif
#ifdef Z_LIMIT_PIN
attachInterrupt(digitalPinToInterrupt(Z_LIMIT_PIN), isr_limit_switches, CHANGE);
#endif
#ifdef A_LIMIT_PIN
attachInterrupt(digitalPinToInterrupt(A_LIMIT_PIN), isr_limit_switches, CHANGE);
#endif
#ifdef B_LIMIT_PIN
attachInterrupt(digitalPinToInterrupt(B_LIMIT_PIN), isr_limit_switches, CHANGE);
#endif
#ifdef C_LIMIT_PIN
attachInterrupt(digitalPinToInterrupt(C_LIMIT_PIN), isr_limit_switches, CHANGE);
#endif
} else
limits_disable();
// setup task used for debouncing // setup task used for debouncing
limit_sw_queue = xQueueCreate(10, sizeof(int)); limit_sw_queue = xQueueCreate(10, sizeof(int));
xTaskCreate(limitCheckTask, xTaskCreate(limitCheckTask,
@@ -323,55 +301,34 @@ void limits_init() {
NULL); NULL);
} }
// Disables hard limits. // Disables hard limits.
void limits_disable() { void limits_disable() {
detachInterrupt(X_LIMIT_BIT); for (int i=0; i<N_AXIS; i++) {
detachInterrupt(Y_LIMIT_BIT); if (limit_pins[i] != UNDEFINED_PIN) {
detachInterrupt(Z_LIMIT_BIT); detachInterrupt(i);
detachInterrupt(A_LIMIT_BIT); }
detachInterrupt(B_LIMIT_BIT); }
detachInterrupt(C_LIMIT_BIT);
} }
// Returns limit state as a bit-wise uint8 variable. Each bit indicates an axis limit, where // Returns limit state as a bit-wise uint8 variable. Each bit indicates an axis limit, where
// triggered is 1 and not triggered is 0. Invert mask is applied. Axes are defined by their // triggered is 1 and not triggered is 0. Invert mask is applied. Axes are defined by their
// number in bit position, i.e. Z_AXIS is (1<<2) or bit 2, and Y_AXIS is (1<<1) or bit 1. // number in bit position, i.e. Z_AXIS is bit(2), and Y_AXIS is bit(1).
uint8_t limits_get_state() { uint8_t limits_get_state() {
uint8_t limit_state = 0; uint8_t pinMask = 0;
uint8_t pin = 0; for (int i=0; i<N_AXIS; i++) {
#ifdef X_LIMIT_PIN uint8_t pin;
pin += digitalRead(X_LIMIT_PIN); if ((pin = limit_pins[i]) != UNDEFINED_PIN) {
#endif pinMask += digitalRead(pin) << i;
#ifdef Y_LIMIT_PIN
pin += (digitalRead(Y_LIMIT_PIN) << Y_AXIS);
#endif
#ifdef Z_LIMIT_PIN
pin += (digitalRead(Z_LIMIT_PIN) << Z_AXIS);
#endif
#ifdef A_LIMIT_PIN
pin += (digitalRead(A_LIMIT_PIN) << A_AXIS);
#endif
#ifdef B_LIMIT_PIN
pin += (digitalRead(B_LIMIT_PIN) << B_AXIS);
#endif
#ifdef C_LIMIT_PIN
pin += (digitalRead(C_LIMIT_PIN) << C_AXIS);
#endif
#ifdef INVERT_LIMIT_PIN_MASK // not normally used..unless you have both normal and inverted switches
pin ^= INVERT_LIMIT_PIN_MASK;
#endif
if (bit_istrue(settings.flags, BITFLAG_INVERT_LIMIT_PINS))
pin ^= LIMIT_MASK;
if (pin) {
uint8_t idx;
for (idx = 0; idx < N_AXIS; idx++) {
if (pin & get_limit_pin_mask(idx))
limit_state |= (1 << idx);
} }
} }
return (limit_state);
#ifdef INVERT_LIMIT_PIN_MASK // not normally used..unless you have both normal and inverted switches
pinMask ^= INVERT_LIMIT_PIN_MASK;
#endif
if (limit_invert->get()) {
pinMask ^= limit_mask;
}
return (pinMask);
} }
// Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed, // Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed,
@@ -380,11 +337,11 @@ uint8_t limits_get_state() {
void limits_soft_check(float* target) { void limits_soft_check(float* target) {
if (system_check_travel_limits(target)) { if (system_check_travel_limits(target)) {
// TODO for debugging only 3 axes right now // TODO for debugging only 3 axes right now
grbl_msg_sendf(CLIENT_SERIAL, grbl_msg_sendf(CLIENT_SERIAL,
MSG_LEVEL_INFO, MSG_LEVEL_INFO,
"Soft limit error target WPOS X:%5.2f Y:%5.2f Z:%5.2f", "Soft limit error target WPOS X:%5.2f Y:%5.2f Z:%5.2f",
target[X_AXIS] - gc_state.coord_system[X_AXIS], target[X_AXIS] - gc_state.coord_system[X_AXIS],
target[Y_AXIS] - gc_state.coord_system[Y_AXIS], target[Y_AXIS] - gc_state.coord_system[Y_AXIS],
target[Z_AXIS] - gc_state.coord_system[Z_AXIS]); target[Z_AXIS] - gc_state.coord_system[Z_AXIS]);
sys.soft_limit = true; sys.soft_limit = true;
@@ -439,7 +396,22 @@ bool axis_is_squared(uint8_t axis_mask) {
if (axis_mask == (1 << Z_AXIS)) { if (axis_mask == (1 << Z_AXIS)) {
#ifdef Z_AXIS_SQUARING #ifdef Z_AXIS_SQUARING
return true; return true;
#endif
}
if (axis_mask == (1 << A_AXIS)) {
#ifdef A_AXIS_SQUARING
return true;
#endif
}
if (axis_mask == (1 << B_AXIS)) {
#ifdef B_AXIS_SQUARING
return true;
#endif
}
if (axis_mask == (1 << C_AXIS)) {
#ifdef C_AXIS_SQUARING
return true;
#endif #endif
} }
return false; return false;
} }

View File

@@ -20,11 +20,6 @@
#include "grbl_sd.h" #include "grbl_sd.h"
// Define line flags. Includes comment type tracking and line overflow detection.
#define LINE_FLAG_OVERFLOW bit(0)
#define LINE_FLAG_COMMENT_PARENTHESES bit(1)
#define LINE_FLAG_COMMENT_SEMICOLON bit(2)
File myFile; File myFile;
bool SD_ready_next = false; // Grbl has processed a line and is waiting for another bool SD_ready_next = false; // Grbl has processed a line and is waiting for another
uint8_t SD_client = CLIENT_SERIAL; uint8_t SD_client = CLIENT_SERIAL;
@@ -86,65 +81,31 @@ boolean closeFile() {
} }
/* /*
read a line from the SD card read a line from the SD card
strip whitespace strip whitespace
strip comments per http://linuxcnc.org/docs/ja/html/gcode/overview.html#gcode:comments strip comments per http://linuxcnc.org/docs/ja/html/gcode/overview.html#gcode:comments
make uppercase make uppercase
return true if a line is return true if a line is
*/ */
boolean readFileLine(char* line) { boolean readFileLine(char* line, int maxlen) {
char c;
uint8_t index = 0;
uint8_t line_flags = 0;
uint8_t comment_char_counter = 0;
if (!myFile) { if (!myFile) {
report_status_message(STATUS_SD_FAILED_READ, SD_client); report_status_message(STATUS_SD_FAILED_READ, SD_client);
return false; return false;
} }
sd_current_line_number += 1; sd_current_line_number += 1;
int len = 0;
while (myFile.available()) { while (myFile.available()) {
c = myFile.read(); if (len >= maxlen) {
if (line_flags & LINE_FLAG_COMMENT_PARENTHESES) // capture all characters into a comment buffer
comment[comment_char_counter++] = c;
if (c == '\r' || c == ' ') {
// ignore these whitespace items
} else if (c == '(')
line_flags |= LINE_FLAG_COMMENT_PARENTHESES;
else if (c == ')') {
// End of '()' comment. Resume line allowed.
if (line_flags & LINE_FLAG_COMMENT_PARENTHESES) {
line_flags &= ~(LINE_FLAG_COMMENT_PARENTHESES);
comment[comment_char_counter] = 0; // null terminate
report_gcode_comment(comment);
}
} else if (c == ';') {
// NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST.
if (!(line_flags & LINE_FLAG_COMMENT_PARENTHESES)) // semi colon inside parentheses do not mean anything
line_flags |= LINE_FLAG_COMMENT_SEMICOLON;
} else if (c == '%') {
// discard this character
} else if (c == '\n') { // found the newline, so mark the end and return true
line[index] = '\0';
return true;
} else { // add characters to the line
if (!line_flags) {
c = toupper(c); // make upper case
line[index] = c;
index++;
}
}
if (index == 255) { // name is too long so return false
line[index] = '\0';
report_status_message(STATUS_OVERFLOW, SD_client);
return false; return false;
} }
char c = myFile.read();
if (c == '\n') {
break;
}
line[len++] = c;
} }
// some files end without a newline line[len] = '\0';
if (index != 0) { return len || myFile.available();
line[index] = '\0';
return true;
} else // empty line after new line
return false;
} }
// return a percentage complete 50.5 = 50.5% // return a percentage complete 50.5 = 50.5%

View File

@@ -44,7 +44,7 @@ uint8_t set_sd_state(uint8_t flag);
void listDir(fs::FS& fs, const char* dirname, uint8_t levels, uint8_t client); void listDir(fs::FS& fs, const char* dirname, uint8_t levels, uint8_t client);
boolean openFile(fs::FS& fs, const char* path); boolean openFile(fs::FS& fs, const char* path);
boolean closeFile(); boolean closeFile();
boolean readFileLine(char* line); boolean readFileLine(char* line, int len);
void readFile(fs::FS& fs, const char* path); void readFile(fs::FS& fs, const char* path);
float sd_report_perc_complete(); float sd_report_perc_complete();
uint32_t sd_get_current_line_number(); uint32_t sd_get_current_line_number();

View File

@@ -164,7 +164,7 @@
#ifdef C_TRINAMIC #ifdef C_TRINAMIC
#ifdef C_DRIVER_TMC2130 #ifdef C_DRIVER_TMC2130
TMC2130Stepper TRINAMIC_c = TMC2130Stepper(C_CS_PIN, C_RSENSE, get_next_trinamic_driver_index()); TMC2130Stepper TRINAMIC_C = TMC2130Stepper(C_CS_PIN, C_RSENSE, get_next_trinamic_driver_index());
#endif #endif
#ifdef C_DRIVER_TMC2209 #ifdef C_DRIVER_TMC2209
TMC2209Stepper TRINAMIC_C = TMC2209Stepper(C_CS_PIN, C_RSENSE, get_next_trinamic_driver_index()); TMC2209Stepper TRINAMIC_C = TMC2209Stepper(C_CS_PIN, C_RSENSE, get_next_trinamic_driver_index());
@@ -187,26 +187,102 @@
void Trinamic_Init() { void Trinamic_Init() {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TMCStepper Init using Library Ver 0x%06x", TMCSTEPPER_VERSION); grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "TMCStepper Init using Library Ver 0x%06x", TMCSTEPPER_VERSION);
// On Slack Grbl_Esp32 channel, Mitch Bradley suggested.
// "Any pin that could possibly be implemented with a GPIO pin needs
// a pinMode before the first active use. pinMode(OUTPUT) does not have to
// precede digitalWrite(HIGH), as the following sequence is useful:
// digitalWrite(GPIOn, HIGH);
// pinMode(GPIOn, OUTPUT);
// The reason for that sequence is that, for pins that default to input mode/tri-state,
// setting it HIGH first prevents a low-going transition if the pin output register
// happens to be LOW when you select OUTPUT mode."
// Notes on using I2S out:
// The TMC connected to the I2S out requires SPI clocking down (approximately 100 KHz)
// to work in concert with slow CS operations by I2S out.
#ifdef X_DRIVER_TMC2130
digitalWrite(X_CS_PIN, HIGH);
pinMode(X_CS_PIN, OUTPUT);
TRINAMIC_X.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef X2_DRIVER_TMC2130
digitalWrite(X2_CS_PIN, HIGH);
pinMode(X2_CS_PIN, OUTPUT);
TRINAMIC_X2.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef Y_DRIVER_TMC2130
digitalWrite(Y_CS_PIN, HIGH);
pinMode(Y_CS_PIN, OUTPUT);
TRINAMIC_Y.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef Y2_DRIVER_TMC2130
digitalWrite(Y2_CS_PIN, HIGH);
pinMode(Y2_CS_PIN, OUTPUT);
TRINAMIC_Y2.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef Z_DRIVER_TMC2130
digitalWrite(Z_CS_PIN, HIGH);
pinMode(Z_CS_PIN, OUTPUT);
TRINAMIC_Z.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef Z2_DRIVER_TMC2130
digitalWrite(Z2_CS_PIN, HIGH);
pinMode(Z2_CS_PIN, OUTPUT);
TRINAMIC_Z2.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef A_DRIVER_TMC2130
digitalWrite(A_CS_PIN, HIGH);
pinMode(A_CS_PIN, OUTPUT);
TRINAMIC_A.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef A2_DRIVER_TMC2130
digitalWrite(A2_CS_PIN, HIGH);
pinMode(A2_CS_PIN, OUTPUT);
TRINAMIC_A2.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef B_DRIVER_TMC2130
digitalWrite(B_CS_PIN, HIGH);
pinMode(B_CS_PIN, OUTPUT);
TRINAMIC_B.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef B2_DRIVER_TMC2130
digitalWrite(B2_CS_PIN, HIGH);
pinMode(B2_CS_PIN, OUTPUT);
TRINAMIC_B2.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef C_DRIVER_TMC2130
digitalWrite(C_CS_PIN, HIGH);
pinMode(C_CS_PIN, OUTPUT);
TRINAMIC_C.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
#ifdef C2_DRIVER_TMC2130
digitalWrite(C2_CS_PIN, HIGH);
pinMode(C2_CS_PIN, OUTPUT);
TRINAMIC_C2.setSPISpeed(TRINAMIC_SPI_FREQ);
#endif
SPI.begin(); SPI.begin();
#ifdef USE_MACHINE_TRINAMIC_INIT #ifdef USE_MACHINE_TRINAMIC_INIT
machine_trinamic_setup(); machine_trinamic_setup();
return; return;
#endif #endif
#ifdef X_TRINAMIC #ifdef X_TRINAMIC
TRINAMIC_X.begin(); // Initiate pins and registries TRINAMIC_X.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_X.test_connection(), "X"); trinamic_test_response(TRINAMIC_X.test_connection(), "X");
TRINAMIC_X.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_X.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_X.microsteps(settings.microsteps[X_AXIS]); TRINAMIC_X.microsteps(axis_settings[X_AXIS]->microsteps->get());
TRINAMIC_X.rms_current(settings.current[X_AXIS] * 1000.0, settings.hold_current[X_AXIS] / 100.0); TRINAMIC_X.rms_current(axis_settings[X_AXIS]->run_current->get() * 1000.0, axis_settings[X_AXIS]->hold_current->get() / 100.0);
TRINAMIC_X.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_X.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_X.pwm_autoscale(1); TRINAMIC_X.pwm_autoscale(1);
#endif #endif
#ifdef X2_TRINAMIC #ifdef X2_TRINAMIC
TRINAMIC_X2.begin(); // Initiate pins and registries TRINAMIC_X2.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_X2.test_connection(), "X2"); trinamic_test_response(TRINAMIC_X2.test_connection(), "X2");
TRINAMIC_X2.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_X2.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_X2.microsteps(settings.microsteps[X_AXIS]); TRINAMIC_X2.microsteps(axis_settings[X_AXIS]->microsteps->get());
TRINAMIC_X2.rms_current(settings.current[X_AXIS] * 1000.0, settings.hold_current[X_AXIS] / 100.0); TRINAMIC_X2.rms_current(axis_settings[X_AXIS]->run_current->get() * 1000.0, axis_settings[X_AXIS]->hold_current->get() / 100.0);
TRINAMIC_X2.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_X2.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_X2.pwm_autoscale(1); TRINAMIC_X2.pwm_autoscale(1);
#endif #endif
@@ -215,8 +291,8 @@ void Trinamic_Init() {
TRINAMIC_Y.begin(); // Initiate pins and registries TRINAMIC_Y.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_Y.test_connection(), "Y"); trinamic_test_response(TRINAMIC_Y.test_connection(), "Y");
TRINAMIC_Y.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_Y.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_Y.microsteps(settings.microsteps[Y_AXIS]); TRINAMIC_Y.microsteps(axis_settings[Y_AXIS]->microsteps->get());
TRINAMIC_Y.rms_current(settings.current[Y_AXIS] * 1000.0, settings.hold_current[Y_AXIS] / 100.0); TRINAMIC_Y.rms_current(axis_settings[Y_AXIS]->run_current->get() * 1000.0, axis_settings[Y_AXIS]->hold_current->get() / 100.0);
TRINAMIC_Y.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_Y.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_Y.pwm_autoscale(1); TRINAMIC_Y.pwm_autoscale(1);
#endif #endif
@@ -224,8 +300,8 @@ void Trinamic_Init() {
TRINAMIC_Y2.begin(); // Initiate pins and registries TRINAMIC_Y2.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_Y2.test_connection(), "Y2"); trinamic_test_response(TRINAMIC_Y2.test_connection(), "Y2");
TRINAMIC_Y2.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_Y2.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_Y2.microsteps(settings.microsteps[Y_AXIS]); TRINAMIC_Y2.microsteps(axis_settings[Y_AXIS]->microsteps->get());
TRINAMIC_Y2.rms_current(settings.current[Y_AXIS] * 1000.0, settings.hold_current[Y_AXIS] / 100.0); TRINAMIC_Y2.rms_current(axis_settings[Y_AXIS]->run_current->get() * 1000.0, axis_settings[Y_AXIS]->hold_current->get() / 100.0);
TRINAMIC_Y2.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_Y2.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_Y2.pwm_autoscale(1); TRINAMIC_Y2.pwm_autoscale(1);
#endif #endif
@@ -234,8 +310,8 @@ void Trinamic_Init() {
TRINAMIC_Z.begin(); // Initiate pins and registries TRINAMIC_Z.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_Z.test_connection(), "Z"); trinamic_test_response(TRINAMIC_Z.test_connection(), "Z");
TRINAMIC_Z.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_Z.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_Z.microsteps(settings.microsteps[Z_AXIS]); TRINAMIC_Z.microsteps(axis_settings[Z_AXIS]->microsteps->get());
TRINAMIC_Z.rms_current(settings.current[Z_AXIS] * 1000.0, settings.hold_current[Z_AXIS] / 100.0); TRINAMIC_Z.rms_current(axis_settings[Z_AXIS]->run_current->get() * 1000.0, axis_settings[Z_AXIS]->hold_current->get() / 100.0);
TRINAMIC_Z.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_Z.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_Z.pwm_autoscale(1); TRINAMIC_Z.pwm_autoscale(1);
#endif #endif
@@ -243,8 +319,8 @@ void Trinamic_Init() {
TRINAMIC_Z2.begin(); // Initiate pins and registries TRINAMIC_Z2.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_Z2.test_connection(), "Z2"); trinamic_test_response(TRINAMIC_Z2.test_connection(), "Z2");
TRINAMIC_Z2.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_Z2.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_Z2.microsteps(settings.microsteps[Z_AXIS]); TRINAMIC_Z2.microsteps(axis_settings[Z_AXIS]->microsteps->get());
TRINAMIC_Z2.rms_current(settings.current[Z_AXIS] * 1000.0, settings.hold_current[Z_AXIS] / 100.0); TRINAMIC_Z2.rms_current(axis_settings[Z_AXIS]->run_current->get() * 1000.0, axis_settings[Z_AXIS]->hold_current->get() / 100.0);
TRINAMIC_Z2.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_Z2.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_Z2.pwm_autoscale(1); TRINAMIC_Z2.pwm_autoscale(1);
#endif #endif
@@ -253,8 +329,8 @@ void Trinamic_Init() {
TRINAMIC_A.begin(); // Initiate pins and registries TRINAMIC_A.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_A.test_connection(), "A"); trinamic_test_response(TRINAMIC_A.test_connection(), "A");
TRINAMIC_A.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_A.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_A.microsteps(settings.microsteps[A_AXIS]); TRINAMIC_A.microsteps(axis_settings[A_AXIS]->microsteps->get());
TRINAMIC_A.rms_current(settings.current[A_AXIS] * 1000.0, settings.hold_current[A_AXIS] / 100.0); TRINAMIC_A.rms_current(axis_settings[A_AXIS]->run_current->get() * 1000.0, axis_settings[A_AXIS]->hold_current->get() / 100.0);
TRINAMIC_A.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_A.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_A.pwm_autoscale(1); TRINAMIC_A.pwm_autoscale(1);
#endif #endif
@@ -262,8 +338,8 @@ void Trinamic_Init() {
TRINAMIC_A2.begin(); // Initiate pins and registries TRINAMIC_A2.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_A2.test_connection(), "A2"); trinamic_test_response(TRINAMIC_A2.test_connection(), "A2");
TRINAMIC_A2.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_A2.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_A2.microsteps(settings.microsteps[A_AXIS]); TRINAMIC_A2.microsteps(axis_settings[A_AXIS]->microsteps->get());
TRINAMIC_A2.rms_current(settings.current[A_AXIS] * 1000.0, settings.hold_current[A_AXIS] / 100.0); TRINAMIC_A2.rms_current(axis_settings[A_AXIS]->run_current->get() * 1000.0, axis_settings[A_AXIS]->hold_current->get() / 100.0);
TRINAMIC_A2.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_A2.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_A2.pwm_autoscale(1); TRINAMIC_A2.pwm_autoscale(1);
#endif #endif
@@ -272,8 +348,8 @@ void Trinamic_Init() {
TRINAMIC_B.begin(); // Initiate pins and registries TRINAMIC_B.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_B.test_connection(), "B"); trinamic_test_response(TRINAMIC_B.test_connection(), "B");
TRINAMIC_B.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_B.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_B.microsteps(settings.microsteps[B_AXIS]); TRINAMIC_B.microsteps(axis_settings[B_AXIS]->microsteps->get());
TRINAMIC_B.rms_current(settings.current[B_AXIS] * 1000.0, settings.hold_current[B_AXIS] / 100.0); TRINAMIC_B.rms_current(axis_settings[B_AXIS]->run_current->get() * 1000.0, axis_settings[B_AXIS]->hold_current->get() / 100.0);
TRINAMIC_B.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_B.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_B.pwm_autoscale(1); TRINAMIC_B.pwm_autoscale(1);
#endif #endif
@@ -281,8 +357,8 @@ void Trinamic_Init() {
TRINAMIC_B2.begin(); // Initiate pins and registries TRINAMIC_B2.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_B2.test_connection(), "B2"); trinamic_test_response(TRINAMIC_B2.test_connection(), "B2");
TRINAMIC_B2.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_B2.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_B2.microsteps(settings.microsteps[B_AXIS]); TRINAMIC_B2.microsteps(axis_settings[B_AXIS]->microsteps->get());
TRINAMIC_B2.rms_current(settings.current[B_AXIS] * 1000.0, settings.hold_current[B_AXIS] / 100.0); TRINAMIC_B2.rms_current(axis_settings[B_AXIS]->run_current->get() * 1000.0, axis_settings[B_AXIS]->hold_current->get() / 100.0);
TRINAMIC_B2.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_B2.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_B2.pwm_autoscale(1); TRINAMIC_B2.pwm_autoscale(1);
#endif #endif
@@ -291,8 +367,8 @@ void Trinamic_Init() {
TRINAMIC_C.begin(); // Initiate pins and registries TRINAMIC_C.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_C.test_connection(), "C"); trinamic_test_response(TRINAMIC_C.test_connection(), "C");
TRINAMIC_C.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_C.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_C.microsteps(settings.microsteps[C_AXIS]); TRINAMIC_C.microsteps(axis_settings[C_AXIS]->microsteps->get());
TRINAMIC_C.rms_current(settings.current[C_AXIS] * 1000.0, settings.hold_current[C_AXIS] / 100.0); TRINAMIC_C.rms_current(axis_settings[C_AXIS]->run_current->get() * 1000.0, axis_settings[C_AXIS]->hold_current->get() / 100.0);
TRINAMIC_C.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_C.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_C.pwm_autoscale(1); TRINAMIC_C.pwm_autoscale(1);
#endif #endif
@@ -300,8 +376,8 @@ void Trinamic_Init() {
TRINAMIC_C2.begin(); // Initiate pins and registries TRINAMIC_C2.begin(); // Initiate pins and registries
trinamic_test_response(TRINAMIC_C2.test_connection(), "C2"); trinamic_test_response(TRINAMIC_C2.test_connection(), "C2");
TRINAMIC_C2.toff(TRINAMIC_DEFAULT_TOFF); TRINAMIC_C2.toff(TRINAMIC_DEFAULT_TOFF);
TRINAMIC_C2.microsteps(settings.microsteps[C_AXIS]); TRINAMIC_C2.microsteps(axis_settings[C_AXIS]->microsteps->get());
TRINAMIC_C2.rms_current(settings.current[C_AXIS] * 1000.0, settings.hold_current[C_AXIS] / 100.0); TRINAMIC_C2.rms_current(axis_settings[C_AXIS]->run_current->get() * 1000.0, axis_settings[C_AXIS]->hold_current->get() / 100.0);
TRINAMIC_C2.en_pwm_mode(1); // Enable extremely quiet stepping TRINAMIC_C2.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_C2.pwm_autoscale(1); TRINAMIC_C2.pwm_autoscale(1);
#endif #endif
@@ -310,58 +386,58 @@ void Trinamic_Init() {
// Call this function called whenever $$ settings that affect the drivers are changed // Call this function called whenever $$ settings that affect the drivers are changed
void trinamic_change_settings() { void trinamic_change_settings() {
#ifdef X_TRINAMIC #ifdef X_TRINAMIC
TRINAMIC_X.microsteps(settings.microsteps[X_AXIS]); TRINAMIC_X.microsteps(axis_settings[X_AXIS]->microsteps->get());
TRINAMIC_X.rms_current(settings.current[X_AXIS] * 1000.0, settings.hold_current[X_AXIS] / 100.0); TRINAMIC_X.rms_current(axis_settings[X_AXIS]->run_current->get() * 1000.0, axis_settings[X_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef X2_TRINAMIC #ifdef X2_TRINAMIC
TRINAMIC_X2.microsteps(settings.microsteps[X_AXIS]); TRINAMIC_X2.microsteps(axis_settings[X_AXIS]->microsteps->get());
TRINAMIC_X2.rms_current(settings.current[X_AXIS] * 1000.0, settings.hold_current[X_AXIS] / 100.0); TRINAMIC_X2.rms_current(axis_settings[X_AXIS]->run_current->get() * 1000.0, axis_settings[X_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef Y_TRINAMIC #ifdef Y_TRINAMIC
TRINAMIC_Y.microsteps(settings.microsteps[Y_AXIS]); TRINAMIC_Y.microsteps(axis_settings[Y_AXIS]->microsteps->get());
TRINAMIC_Y.rms_current(settings.current[Y_AXIS] * 1000.0, settings.hold_current[Y_AXIS] / 100.0); TRINAMIC_Y.rms_current(axis_settings[Y_AXIS]->run_current->get() * 1000.0, axis_settings[Y_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef Y2_TRINAMIC #ifdef Y2_TRINAMIC
TRINAMIC_Y2.microsteps(settings.microsteps[Y_AXIS]); TRINAMIC_Y2.microsteps(axis_settings[Y_AXIS]->microsteps->get());
TRINAMIC_Y2.rms_current(settings.current[Y_AXIS] * 1000.0, settings.hold_current[Y_AXIS] / 100.0); TRINAMIC_Y2.rms_current(axis_settings[Y_AXIS]->run_current->get() * 1000.0, axis_settings[Y_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef Z_TRINAMIC #ifdef Z_TRINAMIC
TRINAMIC_Z.microsteps(settings.microsteps[Z_AXIS]); TRINAMIC_Z.microsteps(axis_settings[Z_AXIS]->microsteps->get());
TRINAMIC_Z.rms_current(settings.current[Z_AXIS] * 1000.0, settings.hold_current[Z_AXIS] / 100.0); TRINAMIC_Z.rms_current(axis_settings[Z_AXIS]->run_current->get() * 1000.0, axis_settings[Z_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef Z2_TRINAMIC #ifdef Z2_TRINAMIC
TRINAMIC_Z2.microsteps(settings.microsteps[Z_AXIS]); TRINAMIC_Z2.microsteps(axis_settings[Z_AXIS]->microsteps->get());
TRINAMIC_Z2.rms_current(settings.current[Z_AXIS] * 1000.0, settings.hold_current[Z_AXIS] / 100.0); TRINAMIC_Z2.rms_current(axis_settings[Z_AXIS]->run_current->get() * 1000.0, axis_settings[Z_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef A_TRINAMIC #ifdef A_TRINAMIC
TRINAMIC_A.microsteps(settings.microsteps[A_AXIS]); TRINAMIC_A.microsteps(axis_settings[A_AXIS]->microsteps->get());
TRINAMIC_A.rms_current(settings.current[A_AXIS] * 1000.0, settings.hold_current[A_AXIS] / 100.0); TRINAMIC_A.rms_current(axis_settings[A_AXIS]->run_current->get() * 1000.0, axis_settings[A_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef A2_TRINAMIC #ifdef A2_TRINAMIC
TRINAMIC_A2.microsteps(settings.microsteps[A_AXIS]); TRINAMIC_A2.microsteps(axis_settings[A_AXIS]->microsteps->get());
TRINAMIC_A2.rms_current(settings.current[A_AXIS] * 1000.0, settings.hold_current[A_AXIS] / 100.0); TRINAMIC_A2.rms_current(axis_settings[A_AXIS]->run_current->get() * 1000.0, axis_settings[A_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef B_TRINAMIC #ifdef B_TRINAMIC
TRINAMIC_B.microsteps(settings.microsteps[B_AXIS]); TRINAMIC_B.microsteps(axis_settings[B_AXIS]->microsteps->get());
TTRINAMIC_B.rms_current(settings.current[B_AXIS] * 1000.0, settings.hold_current[B_AXIS] / 100.0); TRINAMIC_B.rms_current(axis_settings[B_AXIS]->run_current->get() * 1000.0, axis_settings[B_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef B2_TRINAMIC #ifdef B2_TRINAMIC
TRINAMIC_B2.microsteps(settings.microsteps[B_AXIS]); TRINAMIC_B2.microsteps(axis_settings[B_AXIS]->microsteps->get());
TTRINAMIC_B2.rms_current(settings.current[B_AXIS] * 1000.0, settings.hold_current[B_AXIS] / 100.0); TRINAMIC_B2.rms_current(axis_settings[B_AXIS]->run_current->get() * 1000.0, axis_settings[B_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef C_TRINAMIC #ifdef C_TRINAMIC
TRINAMIC_C.microsteps(settings.microsteps[C_AXIS]); TRINAMIC_C.microsteps(axis_settings[C_AXIS]->microsteps->get());
TRINAMIC_C.rms_current(settings.current[C_AXIS] * 1000.0, settings.hold_current[C_AXIS] / 100.0); TRINAMIC_C.rms_current(axis_settings[C_AXIS]->run_current->get() * 1000.0, axis_settings[C_AXIS]->hold_current->get() / 100.0);
#endif #endif
#ifdef C2_TRINAMIC #ifdef C2_TRINAMIC
TRINAMIC_C2.microsteps(settings.microsteps[C_AXIS]); TRINAMIC_C2.microsteps(axis_settings[C_AXIS]->microsteps->get());
TRINAMIC_C2.rms_current(settings.current[C_AXIS] * 1000.0, settings.hold_current[C_AXIS] / 100.0); TRINAMIC_C2.rms_current(axis_settings[C_AXIS]->run_current->get() * 1000.0, axis_settings[C_AXIS]->hold_current->get() / 100.0);
#endif #endif
} }
@@ -440,12 +516,21 @@ void trinamic_stepper_enable(bool enable) {
// returns the next spi index. We cannot preassign to axes because ganged (X2 type axes) might // returns the next spi index. We cannot preassign to axes because ganged (X2 type axes) might
// need to be inserted into the order of axes. // need to be inserted into the order of axes.
uint8_t get_next_trinamic_driver_index() { uint8_t get_next_trinamic_driver_index() {
#ifdef TRINAMIC_DAISY_CHAIN
static uint8_t index = 1; // they start at 1 static uint8_t index = 1; // they start at 1
#ifndef TRINAMIC_DAISY_CHAIN return index++;
return -1; #else
#else return -1;
return index++; #endif
#endif
} }
#ifdef USE_I2S_OUT
//
// Override default function and insert a short delay
//
void TMC2130Stepper::switchCSpin(bool state) {
digitalWrite(_pinCS, state);
delay(I2S_OUT_DELAY_MS);
}
#endif
#endif #endif

View File

@@ -29,6 +29,8 @@
#define TMC2130_RSENSE_DEFAULT 0.11f #define TMC2130_RSENSE_DEFAULT 0.11f
#define TMC5160_RSENSE_DEFAULT 0.075f #define TMC5160_RSENSE_DEFAULT 0.075f
#define TRINAMIC_SPI_FREQ 100000
#ifdef USE_TRINAMIC #ifdef USE_TRINAMIC
#include <TMCStepper.h> // https://github.com/teemuatlut/TMCStepper #include <TMCStepper.h> // https://github.com/teemuatlut/TMCStepper
void Trinamic_Init(); void Trinamic_Init();

View File

@@ -61,13 +61,13 @@ void unipolar_init() {
void unipolar_step(uint8_t step_mask, uint8_t dir_mask) { void unipolar_step(uint8_t step_mask, uint8_t dir_mask) {
#ifdef X_UNIPOLAR #ifdef X_UNIPOLAR
X_Unipolar.step(step_mask & (1 << X_AXIS), dir_mask & (1 << X_AXIS)); X_Unipolar.step(step_mask & bit(X_AXIS), dir_mask & bit(X_AXIS));
#endif #endif
#ifdef Y_UNIPOLAR #ifdef Y_UNIPOLAR
Y_Unipolar.step(step_mask & (1 << Y_AXIS), dir_mask & (1 << Y_AXIS)); Y_Unipolar.step(step_mask & bit(Y_AXIS), dir_mask & bit(Y_AXIS));
#endif #endif
#ifdef Z_UNIPOLAR #ifdef Z_UNIPOLAR
Z_Unipolar.step(step_mask & (1 << Z_AXIS), dir_mask & (1 << ZX_AXIS)); Z_Unipolar.step(step_mask & bit(Z_AXIS), dir_mask & bit(ZX_AXIS));
#endif #endif
} }
@@ -211,4 +211,4 @@ void Unipolar::step(bool step, bool dir_forward) {
digitalWrite(_pin_phase2, _phase[2]); digitalWrite(_pin_phase2, _phase[2]);
digitalWrite(_pin_phase3, _phase[3]); digitalWrite(_pin_phase3, _phase[3]);
} }
#endif #endif

907
Grbl_Esp32/i2s_out.cpp Normal file
View File

@@ -0,0 +1,907 @@
/*
i2s_out.cpp
Part of Grbl_ESP32
Basic GPIO expander using the ESP32 I2S peripheral (output)
2020 - Michiyasu Odaki
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#ifdef USE_I2S_OUT
#include <FreeRTOS.h>
#include <driver/periph_ctrl.h>
#include <rom/lldesc.h>
#include <soc/i2s_struct.h>
#include <freertos/queue.h>
#include <stdatomic.h>
#include "Pins.h"
#include "i2s_out.h"
//
// Configrations for DMA connected I2S
//
// One DMA buffer transfer takes about 2 ms
// I2S_OUT_DMABUF_LEN / I2S_SAMPLE_SIZE x I2S_OUT_USEC_PER_PULSE
// = 2000 / 4 x 4
// = 2000us = 2ms
// If I2S_OUT_DMABUF_COUNT is 5, it will take about 10 ms for all the DMA buffer transfers to finish.
//
// Increasing I2S_OUT_DMABUF_COUNT has the effect of preventing buffer underflow,
// but on the other hand, it leads to a delay with pulse and/or non-pulse-generated I/Os.
// The number of I2S_OUT_DMABUF_COUNT should be chosen carefully.
//
// Reference information:
// FreeRTOS task time slice = portTICK_PERIOD_MS = 1 ms (ESP32 FreeRTOS port)
//
#define I2S_SAMPLE_SIZE 4 /* 4 bytes, 32 bits per sample */
#define DMA_SAMPLE_COUNT I2S_OUT_DMABUF_LEN / I2S_SAMPLE_SIZE /* number of samples per buffer */
#define SAMPLE_SAFE_COUNT (20/I2S_OUT_USEC_PER_PULSE) /* prevent buffer overrun (GRBL's $0 should be less than or equal 20) */
#ifdef USE_I2S_OUT_STREAM
typedef struct {
uint32_t **buffers;
uint32_t *current;
uint32_t rw_pos;
lldesc_t **desc;
xQueueHandle queue;
} i2s_out_dma_t;
static i2s_out_dma_t o_dma;
static intr_handle_t i2s_out_isr_handle;
#endif
// output value
static atomic_uint_least32_t i2s_out_port_data = ATOMIC_VAR_INIT(0);
// inner lock
static portMUX_TYPE i2s_out_spinlock = portMUX_INITIALIZER_UNLOCKED;
#define I2S_OUT_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_out_spinlock)
#define I2S_OUT_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_out_spinlock)
#define I2S_OUT_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_spinlock)
#define I2S_OUT_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_spinlock)
static int i2s_out_initialized = 0;
#ifdef USE_I2S_OUT_STREAM
static volatile uint32_t i2s_out_pulse_period;
static uint32_t i2s_out_remain_time_until_next_pulse; // Time remaining until the next pulse (μsec)
static volatile i2s_out_pulse_func_t i2s_out_pulse_func;
#endif
static uint8_t i2s_out_ws_pin = 255;
static uint8_t i2s_out_bck_pin = 255;
static uint8_t i2s_out_data_pin = 255;
enum i2s_out_pulser_status_t {
PASSTHROUGH = 0, // Static I2S mode.The i2s_out_write() reflected with very little delay
STEPPING, // Streaming step data.
WAITING, // Waiting for the step DMA completion
};
static volatile i2s_out_pulser_status_t i2s_out_pulser_status = PASSTHROUGH;
// outer lock
static portMUX_TYPE i2s_out_pulser_spinlock = portMUX_INITIALIZER_UNLOCKED;
#define I2S_OUT_PULSER_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_out_pulser_spinlock)
#define I2S_OUT_PULSER_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_out_pulser_spinlock)
#define I2S_OUT_PULSER_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_pulser_spinlock)
#define I2S_OUT_PULSER_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_pulser_spinlock)
//
// Internal functions
//
static inline void gpio_matrix_out_check(uint8_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) {
//if pin == 255, do not need to configure
if (gpio != 255) {
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
gpio_set_direction((gpio_num_t)gpio, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT);
gpio_matrix_out(gpio, signal_idx, out_inv, oen_inv);
}
}
static inline void i2s_out_single_data() {
#if I2S_OUT_NUM_BITS == 16
uint32_t port_data = atomic_load(&i2s_out_port_data);
port_data <<= 16; // Shift needed. This specification is not spelled out in the manual.
I2S0.conf_single_data = port_data; // Apply port data in real-time (static I2S)
#else
I2S0.conf_single_data = atomic_load(&i2s_out_port_data); // Apply port data in real-time (static I2S)
#endif
}
static inline void i2s_out_reset_fifo_without_lock() {
I2S0.conf.rx_fifo_reset = 1;
I2S0.conf.rx_fifo_reset = 0;
I2S0.conf.tx_fifo_reset = 1;
I2S0.conf.tx_fifo_reset = 0;
}
static void IRAM_ATTR i2s_out_reset_fifo() {
I2S_OUT_ENTER_CRITICAL();
i2s_out_reset_fifo_without_lock();
I2S_OUT_EXIT_CRITICAL();
}
#ifdef USE_I2S_OUT_STREAM
static int IRAM_ATTR i2s_clear_dma_buffer(lldesc_t *dma_desc, uint32_t port_data) {
uint32_t *buf = (uint32_t*)dma_desc->buf;
for (int i = 0; i < DMA_SAMPLE_COUNT; i++) {
buf[i] = port_data;
}
// Restore the buffer length.
// The length may have been changed short when the data was filled in to prevent buffer overrun.
dma_desc->length = I2S_OUT_DMABUF_LEN;
return 0;
}
static int IRAM_ATTR i2s_clear_o_dma_buffers(uint32_t port_data) {
for (int buf_idx = 0; buf_idx < I2S_OUT_DMABUF_COUNT; buf_idx++) {
// Initialize DMA descriptor
o_dma.desc[buf_idx]->owner = 1;
o_dma.desc[buf_idx]->eof = 1; // set to 1 will trigger the interrupt
o_dma.desc[buf_idx]->sosf = 0;
o_dma.desc[buf_idx]->length = I2S_OUT_DMABUF_LEN;
o_dma.desc[buf_idx]->size = I2S_OUT_DMABUF_LEN;
o_dma.desc[buf_idx]->buf = (uint8_t *) o_dma.buffers[buf_idx];
o_dma.desc[buf_idx]->offset = 0;
o_dma.desc[buf_idx]->qe.stqe_next = (lldesc_t *)((buf_idx < (I2S_OUT_DMABUF_COUNT - 1)) ? (o_dma.desc[buf_idx + 1]) : o_dma.desc[0]);
i2s_clear_dma_buffer(o_dma.desc[buf_idx], port_data);
}
return 0;
}
#endif
static int IRAM_ATTR i2s_out_gpio_attach(uint8_t ws, uint8_t bck, uint8_t data) {
// Route the i2s pins to the appropriate GPIO
gpio_matrix_out_check(data, I2S0O_DATA_OUT23_IDX, 0, 0);
gpio_matrix_out_check(bck, I2S0O_BCK_OUT_IDX, 0, 0);
gpio_matrix_out_check(ws, I2S0O_WS_OUT_IDX, 0, 0);
return 0;
}
#define I2S_OUT_DETACH_PORT_IDX 0x100
static int IRAM_ATTR i2s_out_gpio_detach(uint8_t ws, uint8_t bck, uint8_t data) {
// Route the i2s pins to the appropriate GPIO
gpio_matrix_out_check(ws, I2S_OUT_DETACH_PORT_IDX, 0, 0);
gpio_matrix_out_check(bck, I2S_OUT_DETACH_PORT_IDX, 0, 0);
gpio_matrix_out_check(data, I2S_OUT_DETACH_PORT_IDX, 0, 0);
return 0;
}
static int IRAM_ATTR i2s_out_gpio_shiftout(uint32_t port_data) {
__digitalWrite(i2s_out_ws_pin, LOW);
for (int i = 0; i <I2S_OUT_NUM_BITS; i++) {
__digitalWrite(i2s_out_data_pin, !!(port_data & bit(I2S_OUT_NUM_BITS-1 - i)));
__digitalWrite(i2s_out_bck_pin, HIGH);
__digitalWrite(i2s_out_bck_pin, LOW);
}
__digitalWrite(i2s_out_ws_pin, HIGH); // Latch
return 0;
}
static int IRAM_ATTR i2s_out_stop() {
I2S_OUT_ENTER_CRITICAL();
#ifdef USE_I2S_OUT_STREAM
// Stop FIFO DMA
I2S0.out_link.stop = 1;
// Disconnect DMA from FIFO
I2S0.fifo_conf.dscr_en = 0; //Unset this bit to disable I2S DMA mode. (R/W)
#endif
// stop TX module
I2S0.conf.tx_start = 0;
// Force WS to LOW before detach
// This operation prevents unintended WS edge trigger when detach
__digitalWrite(i2s_out_ws_pin, LOW);
// Now, detach GPIO pin from I2S
i2s_out_gpio_detach(i2s_out_ws_pin, i2s_out_bck_pin, i2s_out_data_pin);
// Force BCK to LOW
// After the TX module is stopped, BCK always seems to be in LOW.
// However, I'm going to do it manually to ensure the BCK's LOW.
__digitalWrite(i2s_out_bck_pin, LOW);
// Transmit recovery data to 74HC595
uint32_t port_data = atomic_load(&i2s_out_port_data); // current expanded port value
i2s_out_gpio_shiftout(port_data);
#ifdef USE_I2S_OUT_STREAM
//clear pending interrupt
I2S0.int_clr.val = I2S0.int_st.val;
#endif
I2S_OUT_EXIT_CRITICAL();
return 0;
}
static int IRAM_ATTR i2s_out_start() {
if (!i2s_out_initialized) {
return -1;
}
I2S_OUT_ENTER_CRITICAL();
// Transmit recovery data to 74HC595
uint32_t port_data = atomic_load(&i2s_out_port_data); // current expanded port value
i2s_out_gpio_shiftout(port_data);
// Attach I2S to specified GPIO pin
i2s_out_gpio_attach(i2s_out_ws_pin, i2s_out_bck_pin, i2s_out_data_pin);
//start DMA link
i2s_out_reset_fifo_without_lock();
#ifdef USE_I2S_OUT_STREAM
if (i2s_out_pulser_status == PASSTHROUGH) {
I2S0.conf_chan.tx_chan_mod = 3; // 3:right+constant 4:left+constant (when tx_msb_right = 1)
I2S0.conf_single_data = port_data;
} else {
I2S0.conf_chan.tx_chan_mod = 4; // 3:right+constant 4:left+constant (when tx_msb_right = 1)
I2S0.conf_single_data = 0;
}
#endif
#ifdef USE_I2S_OUT_STREAM
//reset DMA
I2S0.lc_conf.in_rst = 1;
I2S0.lc_conf.in_rst = 0;
I2S0.lc_conf.out_rst = 1;
I2S0.lc_conf.out_rst = 0;
I2S0.out_link.addr = (uint32_t)o_dma.desc[0];
#endif
I2S0.conf.tx_reset = 1;
I2S0.conf.tx_reset = 0;
I2S0.conf.rx_reset = 1;
I2S0.conf.rx_reset = 0;
I2S0.conf1.tx_stop_en = 1; // BCK and WCK are suppressed while FIFO is empty
#ifdef USE_I2S_OUT_STREAM
// Connect DMA to FIFO
I2S0.fifo_conf.dscr_en = 1; // Set this bit to enable I2S DMA mode. (R/W)
I2S0.int_clr.val = 0xFFFFFFFF;
I2S0.out_link.start = 1;
#endif
I2S0.conf.tx_start = 1;
// Wait for the first FIFO data to prevent the unintentional generation of 0 data
ets_delay_us(20);
I2S0.conf1.tx_stop_en = 0; // BCK and WCK are generated regardless of the FIFO status
I2S_OUT_EXIT_CRITICAL();
return 0;
}
#ifdef USE_I2S_OUT_STREAM
static int IRAM_ATTR i2s_fillout_dma_buffer(lldesc_t *dma_desc) {
uint32_t *buf = (uint32_t*)dma_desc->buf;
o_dma.rw_pos = 0;
// It reuses the oldest (just transferred) buffer with the name "current"
// and fills the buffer for later DMA.
I2S_OUT_PULSER_ENTER_CRITICAL(); // Lock pulser status
if (i2s_out_pulser_status == STEPPING) {
//
// Fillout the buffer for pulse
//
// To avoid buffer overflow, all of the maximum pulse width (normaly about 10us)
// is adjusted to be in a single buffer.
// DMA_SAMPLE_SAFE_COUNT is referred to as the margin value.
// Therefore, if a buffer is close to full and it is time to generate a pulse,
// the generation of the buffer is interrupted (the buffer length is shortened slightly)
// and the pulse generation is postponed until the next buffer is filled.
//
o_dma.rw_pos = 0;
while (o_dma.rw_pos < (DMA_SAMPLE_COUNT - SAMPLE_SAFE_COUNT)) {
// no data to read (buffer empty)
if (i2s_out_remain_time_until_next_pulse < I2S_OUT_USEC_PER_PULSE) {
// pulser status may change in pulse phase func, so I need to check it every time.
if (i2s_out_pulser_status == STEPPING) {
// fillout future DMA buffer (tail of the DMA buffer chains)
if (i2s_out_pulse_func != NULL) {
I2S_OUT_PULSER_EXIT_CRITICAL(); // Temporarily unlocked status lock as it may be locked in pulse callback.
(*i2s_out_pulse_func)(); // should be pushed into buffer max DMA_SAMPLE_SAFE_COUNT
I2S_OUT_PULSER_ENTER_CRITICAL(); // Lock again.
i2s_out_remain_time_until_next_pulse = i2s_out_pulse_period;
if (i2s_out_pulser_status == WAITING) {
// i2s_out_set_passthrough() has called from the pulse function.
// It needs to go into pass-through mode.
// This DMA descriptor must be a tail of the chain.
dma_desc->qe.stqe_next = NULL; // Cut the DMA descriptor ring. This allow us to identify the tail of the buffer.
} else if (i2s_out_pulser_status == PASSTHROUGH) {
// i2s_out_reset() has called during the execution of the pulse function.
// I2S has already in static mode, and buffers has cleared to zero.
// To prevent the pulse function from being called back,
// we assume that the buffer is already full.
i2s_out_remain_time_until_next_pulse = 0; // There is no need to fill the current buffer.
o_dma.rw_pos = DMA_SAMPLE_COUNT; // The buffer is full.
break;
}
continue;
}
}
}
// no pulse data in push buffer (pulse off or idle or callback is not defined)
buf[o_dma.rw_pos++] = atomic_load(&i2s_out_port_data);
if (i2s_out_remain_time_until_next_pulse >= I2S_OUT_USEC_PER_PULSE) {
i2s_out_remain_time_until_next_pulse -= I2S_OUT_USEC_PER_PULSE;
} else {
i2s_out_remain_time_until_next_pulse = 0;
}
}
// set filled length to the DMA descriptor
dma_desc->length = o_dma.rw_pos * I2S_SAMPLE_SIZE;
} else if (i2s_out_pulser_status == WAITING) {
i2s_clear_dma_buffer(dma_desc, 0); // Essentially, no clearing is required. I'll make sure I know when I've written something.
o_dma.rw_pos = 0; // If someone calls i2s_out_push_sample, make sure there is no buffer overflow
dma_desc->qe.stqe_next = NULL; // Cut the DMA descriptor ring. This allow us to identify the tail of the buffer.
} else {
// Stepper paused (passthrough state, static I2S control mode)
// In the passthrough mode, there is no need to fill the buffer with port_data.
i2s_clear_dma_buffer(dma_desc, 0); // Essentially, no clearing is required. I'll make sure I know when I've written something.
o_dma.rw_pos = 0; // If someone calls i2s_out_push_sample, make sure there is no buffer overflow
}
I2S_OUT_PULSER_EXIT_CRITICAL(); // Unlock pulser status
return 0;
}
//
// I2S out DMA Interrupts handler
//
static void IRAM_ATTR i2s_out_intr_handler(void *arg) {
lldesc_t *finish_desc;
portBASE_TYPE high_priority_task_awoken = pdFALSE;
if (I2S0.int_st.out_eof || I2S0.int_st.out_total_eof) {
if (I2S0.int_st.out_total_eof) {
// This is tail of the DMA descriptors
I2S_OUT_ENTER_CRITICAL_ISR();
// Stop FIFO DMA
I2S0.out_link.stop = 1;
// Disconnect DMA from FIFO
I2S0.fifo_conf.dscr_en = 0; //Unset this bit to disable I2S DMA mode. (R/W)
// Stop TX module
I2S0.conf.tx_start = 0;
I2S_OUT_EXIT_CRITICAL_ISR();
}
// Get the descriptor of the last item in the linkedlist
finish_desc = (lldesc_t*) I2S0.out_eof_des_addr;
// If the queue is full it's because we have an underflow,
// more than buf_count isr without new data, remove the front buffer
if (xQueueIsQueueFullFromISR(o_dma.queue)) {
lldesc_t *front_desc;
// Remove a descriptor from the DMA complete event queue
xQueueReceiveFromISR(o_dma.queue, &front_desc, &high_priority_task_awoken);
I2S_OUT_PULSER_ENTER_CRITICAL_ISR();
uint32_t port_data = 0;
if (i2s_out_pulser_status == STEPPING) {
port_data = atomic_load(&i2s_out_port_data);
}
I2S_OUT_PULSER_EXIT_CRITICAL_ISR();
for (int i = 0; i < DMA_SAMPLE_COUNT; i++) {
front_desc->buf[i] = port_data;
}
front_desc->length = I2S_OUT_DMABUF_LEN;
}
// Send a DMA complete event to the I2S bitstreamer task with finished buffer
xQueueSendFromISR(o_dma.queue, &finish_desc, &high_priority_task_awoken);
}
if (high_priority_task_awoken == pdTRUE) portYIELD_FROM_ISR();
// clear interrupt
I2S0.int_clr.val = I2S0.int_st.val; //clear pending interrupt
}
//
// I2S bitstream generator task
//
static void IRAM_ATTR i2sOutTask(void* parameter) {
lldesc_t *dma_desc;
while (1) {
// Wait a DMA complete event from I2S isr
// (Block until a DMA transfer has complete)
xQueueReceive(o_dma.queue, &dma_desc, portMAX_DELAY);
o_dma.current = (uint32_t*)(dma_desc->buf);
// It reuses the oldest (just transferred) buffer with the name "current"
// and fills the buffer for later DMA.
I2S_OUT_PULSER_ENTER_CRITICAL(); // Lock pulser status
if (i2s_out_pulser_status == STEPPING) {
//
// Fillout the buffer for pulse
//
// To avoid buffer overflow, all of the maximum pulse width (normaly about 10us)
// is adjusted to be in a single buffer.
// DMA_SAMPLE_SAFE_COUNT is referred to as the margin value.
// Therefore, if a buffer is close to full and it is time to generate a pulse,
// the generation of the buffer is interrupted (the buffer length is shortened slightly)
// and the pulse generation is postponed until the next buffer is filled.
//
i2s_fillout_dma_buffer(dma_desc);
dma_desc->length = o_dma.rw_pos * I2S_SAMPLE_SIZE;
} else if (i2s_out_pulser_status == WAITING) {
if (dma_desc->qe.stqe_next == NULL) {
// Tail of the DMA descriptor found
// I2S TX module has alrewdy stopped by ISR
i2s_out_stop();
i2s_clear_o_dma_buffers(0); // 0 for static I2S control mode (right ch. data is always 0)
// You need to set the status before calling i2s_out_start()
// because the process in i2s_out_start() is different depending on the status.
i2s_out_pulser_status = PASSTHROUGH;
i2s_out_start();
} else {
// Processing a buffer slightly ahead of the tail buffer.
// We don't need to fill up the buffer by port_data any more.
i2s_clear_dma_buffer(dma_desc, 0); // Essentially, no clearing is required. I'll make sure I know when I've written something.
o_dma.rw_pos = 0; // If someone calls i2s_out_push_sample, make sure there is no buffer overflow
dma_desc->qe.stqe_next = NULL; // Cut the DMA descriptor ring. This allow us to identify the tail of the buffer.
}
} else {
// Stepper paused (passthrough state, static I2S control mode)
// In the passthrough mode, there is no need to fill the buffer with port_data.
i2s_clear_dma_buffer(dma_desc, 0); // Essentially, no clearing is required. I'll make sure I know when I've written something.
o_dma.rw_pos = 0; // If someone calls i2s_out_push_sample, make sure there is no buffer overflow
}
I2S_OUT_PULSER_EXIT_CRITICAL(); // Unlock pulser status
}
}
#endif
//
// External funtions
//
void IRAM_ATTR i2s_out_delay() {
#ifdef USE_I2S_OUT_STREAM
I2S_OUT_PULSER_ENTER_CRITICAL();
if (i2s_out_pulser_status == PASSTHROUGH) {
// Depending on the timing, it may not be reflected immediately,
// so wait twice as long just in case.
ets_delay_us(I2S_OUT_USEC_PER_PULSE * 2);
} else {
// Just wait until the data now registered in the DMA descripter
// is reflected in the I2S TX module via FIFO.
delay(I2S_OUT_DELAY_MS);
}
I2S_OUT_PULSER_EXIT_CRITICAL();
#else
ets_delay_us(I2S_OUT_USEC_PER_PULSE * 2);
#endif
}
void IRAM_ATTR i2s_out_write(uint8_t pin, uint8_t val) {
uint32_t bit = bit(pin);
if (val) {
atomic_fetch_or(&i2s_out_port_data, bit);
} else {
atomic_fetch_and(&i2s_out_port_data, ~bit);
}
#ifdef USE_I2S_OUT_STREAM
// It needs a lock for access, but I've given up because I need speed.
// This is not a problem as long as there is no overlap between the status change and digitalWrite().
if (i2s_out_pulser_status == PASSTHROUGH) {
i2s_out_single_data();
}
#else
i2s_out_single_data();
#endif
}
uint8_t IRAM_ATTR i2s_out_state(uint8_t pin) {
uint32_t port_data = atomic_load(&i2s_out_port_data);
return (!!(port_data & bit(pin)));
}
uint32_t IRAM_ATTR i2s_out_push_sample(uint32_t num) {
#ifdef USE_I2S_OUT_STREAM
if (num > SAMPLE_SAFE_COUNT) {
return 0;
}
// push at least one sample (even if num is zero)
uint32_t port_data = atomic_load(&i2s_out_port_data);
uint32_t n = 0;
do {
o_dma.current[o_dma.rw_pos++] = port_data;
n++;
} while(n < num);
return n;
#else
return 0;
#endif
}
int IRAM_ATTR i2s_out_set_passthrough() {
I2S_OUT_PULSER_ENTER_CRITICAL();
#ifdef USE_I2S_OUT_STREAM
if (i2s_out_pulser_status == STEPPING) {
i2s_out_pulser_status = WAITING; // Start stopping the pulser
}
#else
i2s_out_pulser_status = PASSTHROUGH;
#endif
I2S_OUT_PULSER_EXIT_CRITICAL();
return 0;
}
int IRAM_ATTR i2s_out_set_stepping() {
I2S_OUT_PULSER_ENTER_CRITICAL();
#ifdef USE_I2S_OUT_STREAM
if (i2s_out_pulser_status == STEPPING) {
// Re-entered (fail safe)
I2S_OUT_PULSER_EXIT_CRITICAL();
return 0;
}
if (i2s_out_pulser_status == WAITING) {
// Wait for complete DMAs
for(;;) {
I2S_OUT_PULSER_EXIT_CRITICAL();
delay(I2S_OUT_DELAY_DMABUF_MS);
I2S_OUT_PULSER_ENTER_CRITICAL();
if (i2s_out_pulser_status == WAITING) {
continue;
}
if (i2s_out_pulser_status == PASSTHROUGH) {
// DMA completed
break;
}
// Another function change the I2S state to STEPPING
I2S_OUT_PULSER_EXIT_CRITICAL();
return 0;
}
}
// Change I2S state from PASSTHROUGH to STEPPING
i2s_out_stop();
uint32_t port_data = atomic_load(&i2s_out_port_data);
i2s_clear_o_dma_buffers(port_data);
// You need to set the status before calling i2s_out_start()
// because the process in i2s_out_start() is different depending on the status.
i2s_out_pulser_status = STEPPING;
i2s_out_start();
#else
i2s_out_pulser_status = STEPPING;
#endif
I2S_OUT_PULSER_EXIT_CRITICAL();
return 0;
}
int IRAM_ATTR i2s_out_set_pulse_period(uint32_t period) {
#ifdef USE_I2S_OUT_STREAM
i2s_out_pulse_period = period;
#endif
return 0;
}
int IRAM_ATTR i2s_out_set_pulse_callback(i2s_out_pulse_func_t func) {
#ifdef USE_I2S_OUT_STREAM
i2s_out_pulse_func = func;
#endif
return 0;
}
int IRAM_ATTR i2s_out_reset() {
I2S_OUT_PULSER_ENTER_CRITICAL();
i2s_out_stop();
#ifdef USE_I2S_OUT_STREAM
if (i2s_out_pulser_status == STEPPING) {
uint32_t port_data = atomic_load(&i2s_out_port_data);
i2s_clear_o_dma_buffers(port_data);
} else if (i2s_out_pulser_status == WAITING) {
i2s_clear_o_dma_buffers(0);
i2s_out_pulser_status = PASSTHROUGH;
}
#endif
// You need to set the status before calling i2s_out_start()
// because the process in i2s_out_start() is different depending on the status.
i2s_out_start();
I2S_OUT_PULSER_EXIT_CRITICAL();
return 0;
}
//
// Initialize funtion (external function)
//
int IRAM_ATTR i2s_out_init(i2s_out_init_t &init_param) {
if (i2s_out_initialized) {
// already initialized
return -1;
}
atomic_store(&i2s_out_port_data, init_param.init_val);
// To make sure hardware is enabled before any hardware register operations.
periph_module_reset(PERIPH_I2S0_MODULE);
periph_module_enable(PERIPH_I2S0_MODULE);
// Route the i2s pins to the appropriate GPIO
i2s_out_gpio_attach(init_param.ws_pin, init_param.bck_pin, init_param.data_pin);
/**
* Each i2s transfer will take
* fpll = PLL_D2_CLK -- clka_en = 0
*
* fi2s = fpll / N + b/a -- N + b/a = clkm_div_num
* fi2s = 160MHz / 2
* fi2s = 80MHz
*
* fbclk = fi2s / M -- M = tx_bck_div_num
* fbclk = 80MHz / 2
* fbclk = 40MHz
*
* fwclk = fbclk / 32
*
* for fwclk = 250kHz(16-bit: 4µS pulse time), 125kHz(32-bit: 8μS pulse time)
* N = 10, b/a = 0
* M = 2
* for fwclk = 500kHz(16-bit: 2µS pulse time), 250kHz(32-bit: 4μS pulse time)
* N = 5, b/a = 0
* M = 2
* for fwclk = 1000kHz(16-bit: 1µS pulse time), 500kHz(32-bit: 2μS pulse time)
* N = 2, b/a = 2/1 (N + b/a = 2.5)
* M = 2
*/
#ifdef USE_I2S_OUT_STREAM
// Allocate the array of pointers to the buffers
o_dma.buffers = (uint32_t **)malloc(sizeof(uint32_t*) * I2S_OUT_DMABUF_COUNT);
if (o_dma.buffers == nullptr) return -1;
// Allocate each buffer that can be used by the DMA controller
for (int buf_idx = 0; buf_idx < I2S_OUT_DMABUF_COUNT; buf_idx++) {
o_dma.buffers[buf_idx] = (uint32_t*) heap_caps_calloc(1, I2S_OUT_DMABUF_LEN, MALLOC_CAP_DMA);
if (o_dma.buffers[buf_idx] == nullptr) return -1;
}
// Allocate the array of DMA descriptors
o_dma.desc = (lldesc_t**) malloc(sizeof(lldesc_t*) * I2S_OUT_DMABUF_COUNT);
if (o_dma.desc == nullptr) return -1;
// Allocate each DMA descriptor that will be used by the DMA controller
for (int buf_idx = 0; buf_idx < I2S_OUT_DMABUF_COUNT; buf_idx++) {
o_dma.desc[buf_idx] = (lldesc_t*) heap_caps_malloc(sizeof(lldesc_t), MALLOC_CAP_DMA);
if (o_dma.desc[buf_idx] == nullptr) return -1;
}
// Initialize
i2s_clear_o_dma_buffers(init_param.init_val);
o_dma.rw_pos = 0;
o_dma.current = NULL;
o_dma.queue = xQueueCreate(I2S_OUT_DMABUF_COUNT, sizeof(uint32_t *));
// Set the first DMA descriptor
I2S0.out_link.addr = (uint32_t)o_dma.desc[0];
#endif
// stop i2s
I2S0.out_link.stop = 1;
I2S0.conf.tx_start = 0;
I2S0.int_clr.val = I2S0.int_st.val; //clear pending interrupt
//
// i2s_param_config
//
// configure I2S data port interface.
i2s_out_reset_fifo();
//reset i2s
I2S0.conf.tx_reset = 1;
I2S0.conf.tx_reset = 0;
I2S0.conf.rx_reset = 1;
I2S0.conf.rx_reset = 0;
//reset dma
I2S0.lc_conf.in_rst = 1; // Set this bit to reset in DMA FSM. (R/W)
I2S0.lc_conf.in_rst = 0;
I2S0.lc_conf.out_rst = 1; // Set this bit to reset out DMA FSM. (R/W)
I2S0.lc_conf.out_rst = 0;
//Enable and configure DMA
I2S0.lc_conf.check_owner = 0;
I2S0.lc_conf.out_loop_test = 0;
I2S0.lc_conf.out_auto_wrback = 0; // Disable auto outlink-writeback when all the data has been transmitted
I2S0.lc_conf.out_data_burst_en = 0;
I2S0.lc_conf.outdscr_burst_en = 0;
I2S0.lc_conf.out_no_restart_clr = 0;
I2S0.lc_conf.indscr_burst_en = 0;
#ifdef USE_I2S_OUT_STREAM
I2S0.lc_conf.out_eof_mode = 1; // I2S_OUT_EOF_INT generated when DMA has popped all data from the FIFO;
#endif
I2S0.conf2.lcd_en = 0;
I2S0.conf2.camera_en = 0;
I2S0.pdm_conf.pcm2pdm_conv_en = 0;
I2S0.pdm_conf.pdm2pcm_conv_en = 0;
I2S0.fifo_conf.dscr_en = 0;
#ifdef USE_I2S_OUT_STREAM
if (i2s_out_pulser_status == STEPPING) {
// Stream output mode
I2S0.conf_chan.tx_chan_mod = 4; // 3:right+constant 4:left+constant (when tx_msb_right = 1)
I2S0.conf_single_data = 0;
} else {
// Static output mode
I2S0.conf_chan.tx_chan_mod = 3; // 3:right+constant 4:left+constant (when tx_msb_right = 1)
I2S0.conf_single_data = init_param.init_val;
}
#else
// For the static output mode
I2S0.conf_chan.tx_chan_mod = 3; // 3:right+constant 4:left+constant (when tx_msb_right = 1)
I2S0.conf_single_data = init_param.init_val; // initial constant value
#endif
#if I2S_OUT_NUM_BITS == 16
I2S0.fifo_conf.tx_fifo_mod = 0; // 0: 16-bit dual channel data, 3: 32-bit single channel data
I2S0.fifo_conf.rx_fifo_mod = 0; // 0: 16-bit dual channel data, 3: 32-bit single channel data
I2S0.sample_rate_conf.tx_bits_mod = 16; // default is 16-bits
I2S0.sample_rate_conf.rx_bits_mod = 16; // default is 16-bits
#else
I2S0.fifo_conf.tx_fifo_mod = 3; // 0: 16-bit dual channel data, 3: 32-bit single channel data
I2S0.fifo_conf.rx_fifo_mod = 3; // 0: 16-bit dual channel data, 3: 32-bit single channel data
// Data width is 32-bit. Forgetting this setting will result in a 16-bit transfer.
I2S0.sample_rate_conf.tx_bits_mod = 32;
I2S0.sample_rate_conf.rx_bits_mod = 32;
#endif
I2S0.conf.tx_mono = 0; // Set this bit to enable transmitters mono mode in PCM standard mode.
I2S0.conf_chan.rx_chan_mod = 1; // 1: right+right
I2S0.conf.rx_mono = 0;
#ifdef USE_I2S_OUT_STREAM
I2S0.fifo_conf.dscr_en = 1; //connect DMA to fifo
#endif
I2S0.conf.tx_start = 0;
I2S0.conf.rx_start = 0;
I2S0.conf.tx_msb_right = 1; // Set this bit to place right-channel data at the MSB in the transmit FIFO.
I2S0.conf.tx_right_first = 0; // Setting this bit allows the right-channel data to be sent first.
I2S0.conf.tx_slave_mod = 0; // Master
#ifdef USE_I2S_OUT_STREAM
I2S0.fifo_conf.tx_fifo_mod_force_en = 1; //The bit should always be set to 1.
#endif
I2S0.pdm_conf.rx_pdm_en = 0; // Set this bit to enable receivers PDM mode.
I2S0.pdm_conf.tx_pdm_en = 0; // Set this bit to enable transmitters PDM mode.
// I2S_COMM_FORMAT_I2S_LSB
I2S0.conf.tx_short_sync = 0; // Set this bit to enable transmitter in PCM standard mode.
I2S0.conf.rx_short_sync = 0; // Set this bit to enable receiver in PCM standard mode.
I2S0.conf.tx_msb_shift = 0; // Do not use the Philips standard to avoid bit-shifting
I2S0.conf.rx_msb_shift = 0; // Do not use the Philips standard to avoid bit-shifting
//
// i2s_set_clk
//
// set clock (fi2s) 160MHz / 5
I2S0.clkm_conf.clka_en = 0; // Use 160 MHz PLL_D2_CLK as reference
// N + b/a = 0
#if I2S_OUT_NUM_BITS == 16
// N = 10
I2S0.clkm_conf.clkm_div_num = 10; // minimum value of 2, reset value of 4, max 256 (I²S clock dividers integral value)
#else
// N = 5
I2S0.clkm_conf.clkm_div_num = 5; // minimum value of 2, reset value of 4, max 256 (I²S clock dividers integral value)
#endif
// b/a = 0
I2S0.clkm_conf.clkm_div_b = 0; // 0 at reset
I2S0.clkm_conf.clkm_div_a = 0; // 0 at reset, what about divide by 0? (not an issue)
// Bit clock configuration bit in transmitter mode.
// fbck = fi2s / tx_bck_div_num = (160 MHz / 5) / 2 = 16 MHz
I2S0.sample_rate_conf.tx_bck_div_num = 2; // minimum value of 2 defaults to 6
I2S0.sample_rate_conf.rx_bck_div_num = 2;
#ifdef USE_I2S_OUT_STREAM
// Enable TX interrupts (DMA Interrupts)
I2S0.int_ena.out_eof = 1; // Triggered when rxlink has finished sending a packet.
I2S0.int_ena.out_dscr_err = 0; // Triggered when invalid rxlink descriptors are encountered.
I2S0.int_ena.out_total_eof = 1; // Triggered when all transmitting linked lists are used up.
I2S0.int_ena.out_done = 0; // Triggered when all transmitted and buffered data have been read.
// default pulse callback period (μsec)
i2s_out_pulse_period = init_param.pulse_period;
i2s_out_pulse_func = init_param.pulse_func;
// Create the task that will feed the buffer
xTaskCreatePinnedToCore(i2sOutTask,
"I2SOutTask",
1024 * 10,
NULL,
1,
nullptr,
CONFIG_ARDUINO_RUNNING_CORE // must run the task on same core
);
// Allocate and Enable the I2S interrupt
esp_intr_alloc(ETS_I2S0_INTR_SOURCE, 0, i2s_out_intr_handler, nullptr, &i2s_out_isr_handle);
esp_intr_enable(i2s_out_isr_handle);
#endif
// Remember GPIO pin numbers
i2s_out_ws_pin = init_param.ws_pin;
i2s_out_bck_pin = init_param.bck_pin;
i2s_out_data_pin = init_param.data_pin;
i2s_out_initialized = 1;
// Start the I2S peripheral
i2s_out_start();
return 0;
}
#ifndef I2S_OUT_WS
#define I2S_OUT_WS GPIO_NUM_17
#endif
#ifndef I2S_OUT_BCK
#define I2S_OUT_BCK GPIO_NUM_22
#endif
#ifndef I2S_OUT_DATA
#define I2S_OUT_DATA GPIO_NUM_21
#endif
#ifndef I2S_OUT_INIT_VAL
#define I2S_OUT_INIT_VAL 0
#endif
/*
Initialize I2S out by default parameters.
return -1 ... already initialized
*/
int IRAM_ATTR i2s_out_init() {
i2s_out_init_t default_param = {
.ws_pin = I2S_OUT_WS,
.bck_pin = I2S_OUT_BCK,
.data_pin = I2S_OUT_DATA,
.pulse_func = NULL,
.pulse_period = I2S_OUT_USEC_PER_PULSE,
.init_val = I2S_OUT_INIT_VAL,
};
return i2s_out_init(default_param);
}
#endif

191
Grbl_Esp32/i2s_out.h Normal file
View File

@@ -0,0 +1,191 @@
/*
i2s_out.h
Part of Grbl_ESP32
Header for basic GPIO expander using the ESP32 I2S peripheral
2020 - Michiyasu Odaki
Grbl_ESP32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl_ESP32. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef i2s_out_h
#define i2s_out_h
// It should be included at the outset to know the machine configuration.
#include "config.h"
// If USE_I2S_OUT_STREAM is defined
// but the prerequisite USE_I2S_OUT is not defined,
// it is forced to be defined.
#ifdef USE_I2S_OUT_STREAM
#ifndef USE_I2S_OUT
#define USE_I2S_OUT
#endif
#endif
#ifdef USE_I2S_OUT
#include <stdint.h>
/* Assert */
#if defined(I2S_OUT_NUM_BITS)
#if (I2S_OUT_NUM_BITS != 16) && (I2S_OUT_NUM_BITS != 32)
#error "I2S_OUT_NUM_BITS should be 16 or 32"
#endif
#else
#define I2S_OUT_NUM_BITS 32
#endif
#define I2SO(n) (I2S_OUT_PIN_BASE + n)
/* 16-bit mode: 1000000 usec / ((160000000 Hz) / 10 / 2) x 16 bit/pulse x 2(stereo) = 4 usec/pulse */
/* 32-bit mode: 1000000 usec / ((160000000 Hz) / 5 / 2) x 32 bit/pulse x 2(stereo) = 4 usec/pulse */
#define I2S_OUT_USEC_PER_PULSE 4
#define I2S_OUT_DMABUF_COUNT 5 /* number of DMA buffers to store data */
#define I2S_OUT_DMABUF_LEN 2000 /* maximum size in bytes (4092 is DMA's limit) */
#define I2S_OUT_DELAY_DMABUF_MS (I2S_OUT_DMABUF_LEN / sizeof(uint32_t) * I2S_OUT_USEC_PER_PULSE / 1000)
#define I2S_OUT_DELAY_MS (I2S_OUT_DELAY_DMABUF_MS * (I2S_OUT_DMABUF_COUNT + 1))
typedef void (*i2s_out_pulse_func_t)(void);
typedef struct {
/*
I2S bitstream (32-bits): Transfers from MSB(bit31) to LSB(bit0) in sequence
------------------time line------------------------>
Left Channel Right Channel
ws ________________________________~~~~...
bck _~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~...
data vutsrqponmlkjihgfedcba9876543210
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
^
Latches the X bits when ws is switched to High
If I2S_OUT_PIN_BASE is set to 128,
bit0:Expanded GPIO 128, 1: Expanded GPIO 129, ..., v: Expanded GPIO 159
*/
uint8_t ws_pin;
uint8_t bck_pin;
uint8_t data_pin;
i2s_out_pulse_func_t pulse_func;
uint32_t pulse_period; // aka step rate.
uint32_t init_val;
} i2s_out_init_t;
/*
Initialize I2S out by parameters.
return -1 ... already initialized
*/
int i2s_out_init(i2s_out_init_t &init_param);
/*
Initialize I2S out by default parameters.
i2s_out_init_t default_param = {
.ws_pin = I2S_OUT_WS,
.bck_pin = I2S_OUT_BCK,
.data_pin = I2S_OUT_DATA,
.pulse_func = NULL,
.pulse_period = I2S_OUT_USEC_PER_PULSE,
.init_val = I2S_OUT_INIT_VAL,
};
return -1 ... already initialized
*/
int i2s_out_init();
/*
Get a bit state from the internal pin state var.
pin: expanded pin No. (0..31)
*/
uint8_t i2s_out_state(uint8_t pin);
/*
Set a bit in the internal pin state var. (not written electrically)
pin: expanded pin No. (0..31)
val: bit value(0 or not 0)
*/
void i2s_out_write(uint8_t pin, uint8_t val);
/*
Set current pin state to the I2S bitstream buffer
(This call will generate a future I2S_OUT_USEC_PER_PULSE μs x N bitstream)
num: Number of samples to be generated
The number of samples is limited to (20 / I2S_OUT_USEC_PER_PULSE).
return: number of puhsed samples
0 .. no space for push
*/
uint32_t i2s_out_push_sample(uint32_t num);
/*
Set pulser mode to passtrough
After this function is called,
the callback function to generate the pulse data
will not be called.
*/
int i2s_out_set_passthrough();
/*
Set pulser mode to stepping
After this function is called,
the callback function to generate stepping pulse data
will be called.
*/
int i2s_out_set_stepping();
/*
Dynamically delay until the Shift Register Pin changes
according to the current I2S processing state and mode.
*/
void i2s_out_delay();
/*
Set the pulse callback period in microseconds
(like the timer period for the ISR)
*/
int i2s_out_set_pulse_period(uint32_t period);
/*
Register a callback function to generate pulse data
*/
int i2s_out_set_pulse_callback(i2s_out_pulse_func_t func);
/*
Reset i2s I/O expander
- Stop ISR/DMA
- Clear DMA buffer with the current expanded GPIO bits
- Retart ISR/DMA
*/
int i2s_out_reset();
#endif
/*
Reference: "ESP32 Technical Reference Manual" by Espressif Systems
https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
*/
#endif

View File

@@ -33,7 +33,7 @@ uint8_t jog_execute(plan_line_data_t* pl_data, parser_block_t* gc_block) {
#ifdef USE_LINE_NUMBERS #ifdef USE_LINE_NUMBERS
pl_data->line_number = gc_block->values.n; pl_data->line_number = gc_block->values.n;
#endif #endif
if (bit_istrue(settings.flags, BITFLAG_SOFT_LIMIT_ENABLE)) { if (soft_limits->get()) {
if (system_check_travel_limits(gc_block->values.xyz)) return (STATUS_TRAVEL_EXCEEDED); if (system_check_travel_limits(gc_block->values.xyz)) return (STATUS_TRAVEL_EXCEEDED);
} }
// Valid jog command. Plan, set state, and execute. // Valid jog command. Plan, set state, and execute.

View File

@@ -16,6 +16,7 @@ PWM
// !!! For initial testing, start with test_drive.h which disables // !!! For initial testing, start with test_drive.h which disables
// all I/O pins // all I/O pins
// #include "Machines/atari_1020.h"
#include "Machines/test_drive.h" #include "Machines/test_drive.h"
// !!! For actual use, change the line above to select a board // !!! For actual use, change the line above to select a board

View File

@@ -1,7 +1,7 @@
#ifndef _machine_common_h #ifndef _machine_common_h
#define _machine_common_h #define _machine_common_h
#ifndef SPINDLE_TYPE #ifndef SPINDLE_TYPE
#define SPINDLE_TYPE SPINDLE_TYPE_PWM #define SPINDLE_TYPE SPINDLE_TYPE_PWM
#endif #endif
@@ -31,30 +31,8 @@
// =============== Don't change or comment these out ====================== // =============== Don't change or comment these out ======================
// They are for legacy purposes and will not affect your I/O // They are for legacy purposes and will not affect your I/O
#define X_STEP_BIT 0
#define Y_STEP_BIT 1
#define Z_STEP_BIT 2
#define A_STEP_BIT 3
#define B_STEP_BIT 4
#define C_STEP_BIT 5
#define STEP_MASK B111111 #define STEP_MASK B111111
#define X_DIRECTION_BIT 0
#define Y_DIRECTION_BIT 1
#define Z_DIRECTION_BIT 2
#define A_DIRECTION_BIT 3
#define B_DIRECTION_BIT 4
#define C_DIRECTION_BIT 5
#define X_LIMIT_BIT 0
#define Y_LIMIT_BIT 1
#define Z_LIMIT_BIT 2
#define A_LIMIT_BIT 3
#define B_LIMIT_BIT 4
#define C_LIMIT_BIT 5
#define PROBE_MASK 1 #define PROBE_MASK 1
#define CONTROL_MASK B1111
#endif // _machine_common_h #endif // _machine_common_h

View File

@@ -24,6 +24,13 @@
#include "grbl.h" #include "grbl.h"
// M_PI is not defined in standard C/C++ but some compilers
// support it anyway. The following suppresses Intellisense
// problem reports.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
uint8_t ganged_mode = SQUARING_MODE_DUAL; uint8_t ganged_mode = SQUARING_MODE_DUAL;
@@ -46,7 +53,7 @@ void mc_line_kins(float* target, plan_line_data_t* pl_data, float* position) {
void mc_line(float* target, plan_line_data_t* pl_data) { void mc_line(float* target, plan_line_data_t* pl_data) {
// If enabled, check for soft limit violations. Placed here all line motions are picked up // If enabled, check for soft limit violations. Placed here all line motions are picked up
// from everywhere in Grbl. // from everywhere in Grbl.
if (bit_istrue(settings.flags, BITFLAG_SOFT_LIMIT_ENABLE)) { if (soft_limits->get()) {
// NOTE: Block jog state. Jogging is a special case and soft limits are handled independently. // NOTE: Block jog state. Jogging is a special case and soft limits are handled independently.
if (sys.state != STATE_JOG) limits_soft_check(target); if (sys.state != STATE_JOG) limits_soft_check(target);
} }
@@ -84,7 +91,7 @@ void mc_line(float* target, plan_line_data_t* pl_data) {
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used // the direction of helical travel, radius == circle radius, isclockwise boolean. Used
// for vector transformation direction. // for vector transformation direction.
// The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance // The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance
// of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal // of each segment is configured in the arc_tolerance setting, which is defined to be the maximum normal
// distance from segment to the circle when the end points both lie on the circle. // distance from segment to the circle when the end points both lie on the circle.
void mc_arc(float* target, plan_line_data_t* pl_data, float* position, float* offset, float radius, void mc_arc(float* target, plan_line_data_t* pl_data, float* position, float* offset, float radius,
uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc) { uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc) {
@@ -108,11 +115,11 @@ void mc_arc(float* target, plan_line_data_t* pl_data, float* position, float* of
if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) angular_travel += 2 * M_PI; if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) angular_travel += 2 * M_PI;
} }
// NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to // NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to
// (2x) settings.arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit // (2x) arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit
// is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation. // is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation.
// For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases. // For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases.
uint16_t segments = floor(fabs(0.5 * angular_travel * radius) / uint16_t segments = floor(fabs(0.5 * angular_travel * radius) /
sqrt(settings.arc_tolerance * (2 * radius - settings.arc_tolerance))); sqrt(arc_tolerance->get() * (2 * radius - arc_tolerance->get())));
if (segments) { if (segments) {
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated // Multiply inverse feed_rate to compensate for the fact that this movement is approximated
// by a number of discrete segments. The inverse feed_rate should be correct for the sum of // by a number of discrete segments. The inverse feed_rate should be correct for the sum of
@@ -312,12 +319,14 @@ void mc_homing_cycle(uint8_t cycle_mask) {
#endif #endif
} }
protocol_execute_realtime(); // Check for reset and set system abort. protocol_execute_realtime(); // Check for reset and set system abort.
if (sys.abort) return; // Did not complete. Alarm state set by mc_alarm. if (sys.abort) {
return; // Did not complete. Alarm state set by mc_alarm.
}
// Homing cycle complete! Setup system for normal operation. // Homing cycle complete! Setup system for normal operation.
// ------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------
// Sync gcode parser and planner positions to homed position. // Sync gcode parser and planner positions to homed position.
gc_sync_position(); gc_sync_position();
plan_sync_position(); plan_sync_position();
#ifdef USE_KINEMATICS #ifdef USE_KINEMATICS
// This give kinematics a chance to do something after normal homing // This give kinematics a chance to do something after normal homing
kinematics_post_homing(); kinematics_post_homing();
@@ -447,8 +456,10 @@ void mc_reset() {
} else system_set_exec_alarm(EXEC_ALARM_ABORT_CYCLE); } else system_set_exec_alarm(EXEC_ALARM_ABORT_CYCLE);
st_go_idle(); // Force kill steppers. Position has likely been lost. st_go_idle(); // Force kill steppers. Position has likely been lost.
} }
#ifdef USE_GANGED_AXES
ganged_mode = SQUARING_MODE_DUAL; // in case an error occurred during squaring ganged_mode = SQUARING_MODE_DUAL; // in case an error occurred during squaring
#ifdef USE_I2S_OUT_STREAM
i2s_out_reset();
#endif #endif
} }
} }

View File

@@ -28,15 +28,11 @@
// - https://github.com/CosmicBoris/ESP8266SMTP // - https://github.com/CosmicBoris/ESP8266SMTP
// - https://www.electronicshub.org/send-an-email-using-esp8266/ // - https://www.electronicshub.org/send-an-email-using-esp8266/
#include "config.h"
#ifdef ENABLE_NOTIFICATIONS
#include "grbl.h" #include "grbl.h"
#include "commands.h" #ifdef ENABLE_NOTIFICATIONS
#include "notifications_service.h" #include "notifications_service.h"
#include <WiFiClientSecure.h> #include <WiFiClientSecure.h>
#include <Preferences.h>
#include <base64.h> #include <base64.h>
#include "wificonfig.h"
#define PUSHOVERTIMEOUT 5000 #define PUSHOVERTIMEOUT 5000
#define PUSHOVERSERVER "api.pushover.net" #define PUSHOVERSERVER "api.pushover.net"
@@ -273,28 +269,17 @@ bool NotificationsService::sendLineMSG(const char* title, const char* message) {
} }
//Email#serveraddress:port //Email#serveraddress:port
bool NotificationsService::getPortFromSettings() { bool NotificationsService::getPortFromSettings() {
Preferences prefs; String tmp = notification_ts->get();
String defV = DEFAULT_TOKEN;
prefs.begin(NAMESPACE, true);
String tmp = prefs.getString(NOTIFICATION_TS, defV);
prefs.end();
int pos = tmp.lastIndexOf(':'); int pos = tmp.lastIndexOf(':');
if (pos == -1) if (pos == -1)
return false; return false;
_port = tmp.substring(pos + 1).toInt(); _port = tmp.substring(pos + 1).toInt();
log_d("port : %d", _port); log_d("port : %d", _port);
if (_port > 0) return _port > 0;
return true;
else
return false;
} }
//Email#serveraddress:port //Email#serveraddress:port
bool NotificationsService::getServerAddressFromSettings() { bool NotificationsService::getServerAddressFromSettings() {
Preferences prefs; String tmp = notification_ts->get();
String defV = DEFAULT_TOKEN;
prefs.begin(NAMESPACE, true);
String tmp = prefs.getString(NOTIFICATION_TS, defV);
prefs.end();
int pos1 = tmp.indexOf('#'); int pos1 = tmp.indexOf('#');
int pos2 = tmp.lastIndexOf(':'); int pos2 = tmp.lastIndexOf(':');
if ((pos1 == -1) || (pos2 == -1)) if ((pos1 == -1) || (pos2 == -1))
@@ -306,11 +291,7 @@ bool NotificationsService::getServerAddressFromSettings() {
} }
//Email#serveraddress:port //Email#serveraddress:port
bool NotificationsService::getEmailFromSettings() { bool NotificationsService::getEmailFromSettings() {
Preferences prefs; String tmp = notification_ts->get();
String defV = DEFAULT_TOKEN;
prefs.begin(NAMESPACE, true);
String tmp = prefs.getString(NOTIFICATION_TS, defV);
prefs.end();
int pos = tmp.indexOf('#'); int pos = tmp.indexOf('#');
if (pos == -1) if (pos == -1)
return false; return false;
@@ -322,42 +303,34 @@ bool NotificationsService::getEmailFromSettings() {
bool NotificationsService::begin() { bool NotificationsService::begin() {
bool res = true;
end(); end();
Preferences prefs; _notificationType = notification_type->get();
String defV = DEFAULT_TOKEN;
prefs.begin(NAMESPACE, true);
_notificationType = prefs.getChar(NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_TYPE);
switch (_notificationType) { switch (_notificationType) {
case 0: //no notification = no error but no start case 0: //no notification = no error but no start
return true; return true;
case ESP_PUSHOVER_NOTIFICATION: case ESP_PUSHOVER_NOTIFICATION:
_token1 = prefs.getString(NOTIFICATION_T1, defV); _token1 = notification_t1->get();
_token2 = prefs.getString(NOTIFICATION_T2, defV); _token2 = notification_t2->get();
_port = PUSHOVERPORT; _port = PUSHOVERPORT;
_serveraddress = PUSHOVERSERVER; _serveraddress = PUSHOVERSERVER;
break; break;
case ESP_LINE_NOTIFICATION: case ESP_LINE_NOTIFICATION:
_token1 = prefs.getString(NOTIFICATION_T1, defV); _token1 = notification_t1->get();
_port = LINEPORT; _port = LINEPORT;
_serveraddress = LINESERVER; _serveraddress = LINESERVER;
break; break;
case ESP_EMAIL_NOTIFICATION: case ESP_EMAIL_NOTIFICATION:
_token1 = base64::encode(prefs.getString(NOTIFICATION_T1, defV)); _token1 = base64::encode(notification_t1->get());
_token2 = base64::encode(prefs.getString(NOTIFICATION_T2, defV)); _token2 = base64::encode(notification_t2->get());
//log_d("%s",Settings_ESP3D::read_string(ESP_NOTIFICATION_TOKEN1));
//log_d("%s",Settings_ESP3D::read_string(ESP_NOTIFICATION_TOKEN2));
if (!getEmailFromSettings() || !getPortFromSettings() || !getServerAddressFromSettings()) { if (!getEmailFromSettings() || !getPortFromSettings() || !getServerAddressFromSettings()) {
prefs.end();
return false; return false;
} }
break; break;
default: default:
prefs.end();
return false; return false;
break; break;
} }
prefs.end(); bool res = true;
if (WiFi.getMode() != WIFI_STA) if (WiFi.getMode() != WIFI_STA)
res = false; res = false;
if (!res) if (!res)

View File

@@ -36,8 +36,8 @@
// Scientific notation is officially not supported by g-code, and the 'E' character may // Scientific notation is officially not supported by g-code, and the 'E' character may
// be a g-code word on some CNC systems. So, 'E' notation will not be recognized. // be a g-code word on some CNC systems. So, 'E' notation will not be recognized.
// NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod(). // NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod().
uint8_t read_float(char* line, uint8_t* char_counter, float* float_ptr) { uint8_t read_float(const char* line, uint8_t* char_counter, float* float_ptr) {
char* ptr = line + *char_counter; const char* ptr = line + *char_counter;
unsigned char c; unsigned char c;
// Grab first character and increment pointer. No spaces assumed in line. // Grab first character and increment pointer. No spaces assumed in line.
c = *ptr++; c = *ptr++;
@@ -135,12 +135,26 @@ float convert_delta_vector_to_unit_vector(float* vector) {
return (magnitude); return (magnitude);
} }
float limit_value_by_axis_maximum(float* max_value, float* unit_vec) { float limit_acceleration_by_axis_maximum(float* unit_vec) {
uint8_t idx; uint8_t idx;
float limit_value = SOME_LARGE_VALUE; float limit_value = SOME_LARGE_VALUE;
for (idx = 0; idx < N_AXIS; idx++) { for (idx = 0; idx < N_AXIS; idx++) {
if (unit_vec[idx] != 0) // Avoid divide by zero. if (unit_vec[idx] != 0) // Avoid divide by zero.
limit_value = MIN(limit_value, fabs(max_value[idx] / unit_vec[idx])); limit_value = MIN(limit_value, fabs(axis_settings[idx]->acceleration->get() / unit_vec[idx]));
}
// The acceleration setting is stored and displayed in units of mm/sec^2,
// but used in units of mm/min^2. It suffices to perform the conversion once on
// exit, since the limit computation above is independent of units - it simply
// finds the smallest value.
return (limit_value * SEC_PER_MIN_SQ);
}
float limit_rate_by_axis_maximum(float* unit_vec) {
uint8_t idx;
float limit_value = SOME_LARGE_VALUE;
for (idx = 0; idx < N_AXIS; idx++) {
if (unit_vec[idx] != 0) // Avoid divide by zero.
limit_value = MIN(limit_value, fabs(axis_settings[idx]->max_rate->get() / unit_vec[idx]));
} }
return (limit_value); return (limit_value);
} }

View File

@@ -38,6 +38,18 @@
#define B_AXIS 4 #define B_AXIS 4
#define C_AXIS 5 #define C_AXIS 5
#define MAX_AXES 6
#define MAX_GANGED 2
#define PRIMARY_MOTOR 0
#define GANGED_MOTOR 1
#define X2_AXIS (X_AXIS + MAX_AXES)
#define Y2_AXIS (Y_AXIS + MAX_AXES)
#define Z2_AXIS (Z_AXIS + MAX_AXES)
#define A2_AXIS (A_AXIS + MAX_AXES)
#define B2_AXIS (B_AXIS + MAX_AXES)
#define C2_AXIS (C_AXIS + MAX_AXES)
// CoreXY motor assignments. DO NOT ALTER. // CoreXY motor assignments. DO NOT ALTER.
// NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations. // NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations.
@@ -73,7 +85,7 @@
// Read a floating point value from a string. Line points to the input buffer, char_counter // Read a floating point value from a string. Line points to the input buffer, char_counter
// is the indexer pointing to the current character of the line, while float_ptr is // is the indexer pointing to the current character of the line, while float_ptr is
// a pointer to the result variable. Returns true when it succeeds // a pointer to the result variable. Returns true when it succeeds
uint8_t read_float(char* line, uint8_t* char_counter, float* float_ptr); uint8_t read_float(const char* line, uint8_t* char_counter, float* float_ptr);
// Non-blocking delay function used for general operation and suspend features. // Non-blocking delay function used for general operation and suspend features.
void delay_sec(float seconds, uint8_t mode); void delay_sec(float seconds, uint8_t mode);
@@ -85,13 +97,15 @@ void delay_ms(uint16_t ms);
float hypot_f(float x, float y); float hypot_f(float x, float y);
float convert_delta_vector_to_unit_vector(float* vector); float convert_delta_vector_to_unit_vector(float* vector);
float limit_value_by_axis_maximum(float* max_value, float* unit_vec); float limit_acceleration_by_axis_maximum(float* unit_vec);
float limit_rate_by_axis_maximum(float* unit_vec);
float mapConstrain(float x, float in_min, float in_max, float out_min, float out_max); float mapConstrain(float x, float in_min, float in_max, float out_min, float out_max);
float map_float(float x, float in_min, float in_max, float out_min, float out_max); float map_float(float x, float in_min, float in_max, float out_min, float out_max);
uint32_t map_uint32_t(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max); uint32_t map_uint32_t(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max);
float constrain_float(float in, float min, float max); float constrain_float(float in, float min, float max);
bool char_is_numeric(char value); bool char_is_numeric(char value);
char* trim(char* value);
template <class T> void swap(T& a, T& b) { template <class T> void swap(T& a, T& b) {
T c(a); a = b; b = c; T c(a); a = b; b = c;

View File

@@ -304,8 +304,8 @@ uint8_t plan_buffer_line(float* target, plan_line_data_t* pl_data) {
#endif #endif
} else memcpy(position_steps, pl.position, sizeof(pl.position)); } else memcpy(position_steps, pl.position, sizeof(pl.position));
#ifdef COREXY #ifdef COREXY
target_steps[A_MOTOR] = lround(target[A_MOTOR] * settings.steps_per_mm[A_MOTOR]); target_steps[A_MOTOR] = lround(target[A_MOTOR] * axis_settings[A_MOTOR]->steps_per_mm->get());
target_steps[B_MOTOR] = lround(target[B_MOTOR] * settings.steps_per_mm[B_MOTOR]); target_steps[B_MOTOR] = lround(target[B_MOTOR] * axis_settings[B_MOTOR]->steps_per_mm->get());
block->steps[A_MOTOR] = labs((target_steps[X_AXIS] - position_steps[X_AXIS]) + (target_steps[Y_AXIS] - position_steps[Y_AXIS])); block->steps[A_MOTOR] = labs((target_steps[X_AXIS] - position_steps[X_AXIS]) + (target_steps[Y_AXIS] - position_steps[Y_AXIS]));
block->steps[B_MOTOR] = labs((target_steps[X_AXIS] - position_steps[X_AXIS]) - (target_steps[Y_AXIS] - position_steps[Y_AXIS])); block->steps[B_MOTOR] = labs((target_steps[X_AXIS] - position_steps[X_AXIS]) - (target_steps[Y_AXIS] - position_steps[Y_AXIS]));
#endif #endif
@@ -315,21 +315,21 @@ uint8_t plan_buffer_line(float* target, plan_line_data_t* pl_data) {
// NOTE: Computes true distance from converted step values. // NOTE: Computes true distance from converted step values.
#ifdef COREXY #ifdef COREXY
if (!(idx == A_MOTOR) && !(idx == B_MOTOR)) { if (!(idx == A_MOTOR) && !(idx == B_MOTOR)) {
target_steps[idx] = lround(target[idx] * settings.steps_per_mm[idx]); target_steps[idx] = lround(target[idx] * axis_settings[idx]->steps_per_mm->get());
block->steps[idx] = labs(target_steps[idx] - position_steps[idx]); block->steps[idx] = labs(target_steps[idx] - position_steps[idx]);
} }
block->step_event_count = MAX(block->step_event_count, block->steps[idx]); block->step_event_count = MAX(block->step_event_count, block->steps[idx]);
if (idx == A_MOTOR) if (idx == A_MOTOR)
delta_mm = (target_steps[X_AXIS] - position_steps[X_AXIS] + target_steps[Y_AXIS] - position_steps[Y_AXIS]) / settings.steps_per_mm[idx]; delta_mm = (target_steps[X_AXIS] - position_steps[X_AXIS] + target_steps[Y_AXIS] - position_steps[Y_AXIS]) / axis_settings[idx]->steps_per_mm->get();
else if (idx == B_MOTOR) else if (idx == B_MOTOR)
delta_mm = (target_steps[X_AXIS] - position_steps[X_AXIS] - target_steps[Y_AXIS] + position_steps[Y_AXIS]) / settings.steps_per_mm[idx]; delta_mm = (target_steps[X_AXIS] - position_steps[X_AXIS] - target_steps[Y_AXIS] + position_steps[Y_AXIS]) / axis_settings[idx]->steps_per_mm->get();
else else
delta_mm = (target_steps[idx] - position_steps[idx]) / settings.steps_per_mm[idx]; delta_mm = (target_steps[idx] - position_steps[idx]) / axis_settings[idx]->steps_per_mm->get();
#else #else
target_steps[idx] = lround(target[idx] * settings.steps_per_mm[idx]); target_steps[idx] = lround(target[idx] * axis_settings[idx]->steps_per_mm->get());
block->steps[idx] = labs(target_steps[idx] - position_steps[idx]); block->steps[idx] = labs(target_steps[idx] - position_steps[idx]);
block->step_event_count = MAX(block->step_event_count, block->steps[idx]); block->step_event_count = MAX(block->step_event_count, block->steps[idx]);
delta_mm = (target_steps[idx] - position_steps[idx]) / settings.steps_per_mm[idx]; delta_mm = (target_steps[idx] - position_steps[idx]) / axis_settings[idx]->steps_per_mm->get();
#endif #endif
unit_vec[idx] = delta_mm; // Store unit vector numerator unit_vec[idx] = delta_mm; // Store unit vector numerator
// Set direction bits. Bit enabled always means direction is negative. // Set direction bits. Bit enabled always means direction is negative.
@@ -342,8 +342,8 @@ uint8_t plan_buffer_line(float* target, plan_line_data_t* pl_data) {
// NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes, // NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes,
// if they are also orthogonal/independent. Operates on the absolute value of the unit vector. // if they are also orthogonal/independent. Operates on the absolute value of the unit vector.
block->millimeters = convert_delta_vector_to_unit_vector(unit_vec); block->millimeters = convert_delta_vector_to_unit_vector(unit_vec);
block->acceleration = limit_value_by_axis_maximum(settings.acceleration, unit_vec); block->acceleration = limit_acceleration_by_axis_maximum(unit_vec);
block->rapid_rate = limit_value_by_axis_maximum(settings.max_rate, unit_vec); block->rapid_rate = limit_rate_by_axis_maximum(unit_vec);
// Store programmed rate. // Store programmed rate.
if (block->condition & PL_COND_FLAG_RAPID_MOTION) block->programmed_rate = block->rapid_rate; if (block->condition & PL_COND_FLAG_RAPID_MOTION) block->programmed_rate = block->rapid_rate;
else { else {
@@ -394,10 +394,10 @@ uint8_t plan_buffer_line(float* target, plan_line_data_t* pl_data) {
block->max_junction_speed_sqr = SOME_LARGE_VALUE; block->max_junction_speed_sqr = SOME_LARGE_VALUE;
} else { } else {
convert_delta_vector_to_unit_vector(junction_unit_vec); convert_delta_vector_to_unit_vector(junction_unit_vec);
float junction_acceleration = limit_value_by_axis_maximum(settings.acceleration, junction_unit_vec); float junction_acceleration = limit_acceleration_by_axis_maximum(junction_unit_vec);
float sin_theta_d2 = sqrt(0.5 * (1.0 - junction_cos_theta)); // Trig half angle identity. Always positive. float sin_theta_d2 = sqrt(0.5 * (1.0 - junction_cos_theta)); // Trig half angle identity. Always positive.
block->max_junction_speed_sqr = MAX(MINIMUM_JUNCTION_SPEED * MINIMUM_JUNCTION_SPEED, block->max_junction_speed_sqr = MAX(MINIMUM_JUNCTION_SPEED * MINIMUM_JUNCTION_SPEED,
(junction_acceleration * settings.junction_deviation * sin_theta_d2) / (1.0 - sin_theta_d2)); (junction_acceleration * junction_deviation->get() * sin_theta_d2) / (1.0 - sin_theta_d2));
} }
} }
} }

View File

@@ -120,7 +120,7 @@ void printFloat(float n, uint8_t decimal_places) {
// - CoordValue: Handles all position or coordinate values in inches or mm reporting. // - CoordValue: Handles all position or coordinate values in inches or mm reporting.
// - RateValue: Handles feed rate and current velocity in inches or mm reporting. // - RateValue: Handles feed rate and current velocity in inches or mm reporting.
void printFloat_CoordValue(float n) { void printFloat_CoordValue(float n) {
if (bit_istrue(settings.flags, BITFLAG_REPORT_INCHES)) if (report_inches->get())
printFloat(n * INCH_PER_MM, N_DECIMAL_COORDVALUE_INCH); printFloat(n * INCH_PER_MM, N_DECIMAL_COORDVALUE_INCH);
else else
printFloat(n, N_DECIMAL_COORDVALUE_MM); printFloat(n, N_DECIMAL_COORDVALUE_MM);

View File

@@ -46,7 +46,7 @@ void probe_init() {
// and the probing cycle modes for toward-workpiece/away-from-workpiece. // and the probing cycle modes for toward-workpiece/away-from-workpiece.
void probe_configure_invert_mask(uint8_t is_probe_away) { void probe_configure_invert_mask(uint8_t is_probe_away) {
probe_invert_mask = 0; // Initialize as zero. probe_invert_mask = 0; // Initialize as zero.
if (bit_isfalse(settings.flags, BITFLAG_INVERT_PROBE_PIN)) probe_invert_mask ^= PROBE_MASK; if (probe_invert->get()) probe_invert_mask ^= PROBE_MASK;
if (is_probe_away) probe_invert_mask ^= PROBE_MASK; if (is_probe_away) probe_invert_mask ^= PROBE_MASK;
} }

View File

@@ -23,22 +23,70 @@
*/ */
#include "grbl.h" #include "grbl.h"
#include "config.h"
#include "commands.h"
#include "espresponse.h"
// Define line flags. Includes comment type tracking and line overflow detection.
#define LINE_FLAG_OVERFLOW bit(0)
#define LINE_FLAG_COMMENT_PARENTHESES bit(1)
#define LINE_FLAG_COMMENT_SEMICOLON bit(2)
#define LINE_FLAG_BRACKET bit(3) // square bracket for WebUI commands
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
static char comment[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
static void protocol_exec_rt_suspend(); static void protocol_exec_rt_suspend();
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
static char comment[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
static uint8_t line_flags = 0;
static uint8_t char_counter = 0;
static uint8_t comment_char_counter = 0;
typedef struct {
char buffer[LINE_BUFFER_SIZE];
int len;
int line_number;
} client_line_t;
client_line_t client_lines[CLIENT_COUNT];
void empty_line(uint8_t client)
{
client_line_t* cl = &client_lines[client];
cl->len = 0;
cl->buffer[0] = '\0';
}
err_t add_char_to_line(char c, uint8_t client)
{
client_line_t* cl = &client_lines[client];
// Simple editing for interactive input
if (c == '\b') {
// Backspace erases
if (cl->len) {
--cl->len;
cl->buffer[cl->len] = '\0';
}
return STATUS_OK;
}
if (cl->len == (LINE_BUFFER_SIZE - 1)) {
return STATUS_OVERFLOW;
}
if (c == '\r' || c == '\n') {
cl->len = 0;
cl->line_number++;
return STATUS_EOL;
}
cl->buffer[cl->len++] = c;
cl->buffer[cl->len] = '\0';
return STATUS_OK;
}
err_t execute_line(char* line, uint8_t client, auth_t auth_level)
{
err_t result = STATUS_OK;
// Empty or comment line. For syncing purposes.
if (line[0] == 0) {
return STATUS_OK;
}
// Grbl '$' or WebUI '[ESPxxx]' system command
if (line[0] == '$' || line[0] == '[') {
return system_execute_line(line, client, auth_level);
}
// Everything else is gcode. Block if in alarm or jog mode.
if (sys.state & (STATE_ALARM | STATE_JOG)) {
return STATUS_SYSTEM_GC_LOCK;
}
return gc_execute_line(line, client);
}
/* /*
GRBL PRIMARY LOOP: GRBL PRIMARY LOOP:
@@ -47,7 +95,7 @@ void protocol_main_loop() {
//uint8_t client = CLIENT_SERIAL; // default client //uint8_t client = CLIENT_SERIAL; // default client
// Perform some machine checks to make sure everything is good to go. // Perform some machine checks to make sure everything is good to go.
#ifdef CHECK_LIMITS_AT_INIT #ifdef CHECK_LIMITS_AT_INIT
if (bit_istrue(settings.flags, BITFLAG_HARD_LIMIT_ENABLE)) { if (hard_limits->get()) {
if (limits_get_state()) { if (limits_get_state()) {
sys.state = STATE_ALARM; // Ensure alarm state is active. sys.state = STATE_ALARM; // Ensure alarm state is active.
report_feedback_message(MESSAGE_CHECK_LIMITS); report_feedback_message(MESSAGE_CHECK_LIMITS);
@@ -74,15 +122,12 @@ void protocol_main_loop() {
// Primary loop! Upon a system abort, this exits back to main() to reset the system. // Primary loop! Upon a system abort, this exits back to main() to reset the system.
// This is also where Grbl idles while waiting for something to do. // This is also where Grbl idles while waiting for something to do.
// --------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------
uint8_t line_flags = 0;
uint8_t char_counter = 0;
uint8_t comment_char_counter = 0;
uint8_t c; uint8_t c;
for (;;) { for (;;) {
#ifdef ENABLE_SD_CARD #ifdef ENABLE_SD_CARD
if (SD_ready_next) { if (SD_ready_next) {
char fileLine[255]; char fileLine[255];
if (readFileLine(fileLine)) { if (readFileLine(fileLine, 255)) {
SD_ready_next = false; SD_ready_next = false;
report_status_message(gc_execute_line(fileLine, SD_client), SD_client); report_status_message(gc_execute_line(fileLine, SD_client), SD_client);
} else { } else {
@@ -93,102 +138,34 @@ void protocol_main_loop() {
} }
} }
#endif #endif
// Process one line of incoming serial data, as the data becomes available. Performs an // Receive one line of incoming serial data, as the data becomes available.
// initial filtering by removing spaces and comments and capitalizing all letters. // Filtering, if necessary, is done later in gc_execute_line(), so the
// filtering is the same with serial and file input.
uint8_t client = CLIENT_SERIAL; uint8_t client = CLIENT_SERIAL;
char* line;
for (client = 0; client < CLIENT_COUNT; client++) { for (client = 0; client < CLIENT_COUNT; client++) {
while ((c = serial_read(client)) != SERIAL_NO_DATA) { while ((c = serial_read(client)) != SERIAL_NO_DATA) {
if ((c == '\n') || (c == '\r')) { // End of line reached err_t res = add_char_to_line(c, client);
protocol_execute_realtime(); // Runtime command check point. switch (res) {
if (sys.abort) return; // Bail to calling function upon system abort case STATUS_OK:
line[char_counter] = 0; // Set string termination character. break;
#ifdef REPORT_ECHO_LINE_RECEIVED case STATUS_EOL:
report_echo_line_received(line, client); protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) {
return; // Bail to calling function upon system abort
}
line = client_lines[client].buffer;
#ifdef REPORT_ECHO_RAW_LINE_RECEIVED
report_echo_line_received(line, client);
#endif #endif
// Direct and execute one line of formatted input, and report status of execution. // auth_level can be upgraded by supplying a password on the command line
if (line_flags & LINE_FLAG_OVERFLOW) { report_status_message(execute_line(line, client, LEVEL_GUEST), client);
// Report line overflow error. empty_line(client);
break;
case STATUS_OVERFLOW:
report_status_message(STATUS_OVERFLOW, client); report_status_message(STATUS_OVERFLOW, client);
} else if (line[0] == 0) { empty_line(client);
// Empty or comment line. For syncing purposes. break;
report_status_message(STATUS_OK, client);
} else if (line[0] == '$') {
// Grbl '$' system command
report_status_message(system_execute_line(line, client), client);
} else if (line[0] == '[') {
int cmd = 0;
String cmd_params;
if (COMMANDS::check_command(line, &cmd, cmd_params)) {
ESPResponseStream espresponse(client, true);
if (!COMMANDS::execute_internal_command(cmd, cmd_params, LEVEL_GUEST, &espresponse))
report_status_message(STATUS_GCODE_UNSUPPORTED_COMMAND, CLIENT_ALL);
} else grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Unknow Command...%s", line);
} else if (sys.state & (STATE_ALARM | STATE_JOG)) {
// Everything else is gcode. Block if in alarm or jog mode.
report_status_message(STATUS_SYSTEM_GC_LOCK, client);
} else {
// Parse and execute g-code block
report_status_message(gc_execute_line(line, client), client);
}
// Reset tracking data for next line.
line_flags = 0;
char_counter = 0;
comment_char_counter = 0;
} else {
if (line_flags) {
if (line_flags & LINE_FLAG_BRACKET) // in bracket mode all characters are accepted
line[char_counter++] = c;
// Throw away all (except EOL) comment characters and overflow characters.
if (c == ')') {
// End of '()' comment. Resume line allowed.
if (line_flags & LINE_FLAG_COMMENT_PARENTHESES) {
line_flags &= ~(LINE_FLAG_COMMENT_PARENTHESES);
comment[comment_char_counter] = 0; // null terminate
report_gcode_comment(comment);
}
}
if (line_flags & LINE_FLAG_COMMENT_PARENTHESES) // capture all characters into a comment buffer
comment[comment_char_counter++] = c;
} else {
if (c <= ' ') {
// Throw away whitepace and control characters
}
/*
else if (c == '/') {
// Block delete NOT SUPPORTED. Ignore character.
// NOTE: If supported, would simply need to check the system if block delete is enabled.
}
*/
else if (c == '(') {
// Enable comments flag and ignore all characters until ')' or EOL.
// NOTE: This doesn't follow the NIST definition exactly, but is good enough for now.
// In the future, we could simply remove the items within the comments, but retain the
// comment control characters, so that the g-code parser can error-check it.
line_flags |= LINE_FLAG_COMMENT_PARENTHESES;
comment_char_counter = 0;
} else if (c == ';') {
// NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST.
line_flags |= LINE_FLAG_COMMENT_SEMICOLON;
} else if (c == '[') {
// For ESP3D bracket commands like [ESP100]<SSID>pwd=<admin password>
// prevents spaces being striped and converting to uppercase
line_flags |= LINE_FLAG_BRACKET;
line[char_counter++] = c; // capture this character
// TODO: Install '%' feature
} else if (c == '%') {
// Program start-end percent sign NOT SUPPORTED.
// NOTE: This maybe installed to tell Grbl when a program is running vs manual input,
// where, during a program, the system auto-cycle start will continue to execute
// everything until the next '%' sign. This will help fix resuming issues with certain
// functions that empty the planner buffer to execute its task on-time.
} else if (char_counter >= (LINE_BUFFER_SIZE - 1)) {
// Detect line buffer overflow and set flag.
line_flags |= LINE_FLAG_OVERFLOW;
} else if (c >= 'a' && c <= 'z') // Upcase lowercase
line[char_counter++] = c - 'a' + 'A';
else
line[char_counter++] = c;
}
} }
} // while serial read } // while serial read
} // for clients } // for clients
@@ -197,11 +174,14 @@ void protocol_main_loop() {
// completed. In either case, auto-cycle start, if enabled, any queued moves. // completed. In either case, auto-cycle start, if enabled, any queued moves.
protocol_auto_cycle_start(); protocol_auto_cycle_start();
protocol_execute_realtime(); // Runtime command check point. protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) return; // Bail to main() program loop to reset system. if (sys.abort) {
return; // Bail to main() program loop to reset system.
}
// check to see if we should disable the stepper drivers ... esp32 work around for disable in main loop. // check to see if we should disable the stepper drivers ... esp32 work around for disable in main loop.
if (stepper_idle) { if (stepper_idle) {
if (esp_timer_get_time() > stepper_idle_counter) if (esp_timer_get_time() > stepper_idle_counter) {
set_stepper_disable(true); motors_set_disable(true);
}
} }
} }
return; /* Never reached */ return; /* Never reached */
@@ -544,7 +524,7 @@ static void protocol_exec_rt_suspend() {
restore_spindle_speed = block->spindle_speed; restore_spindle_speed = block->spindle_speed;
} }
#ifdef DISABLE_LASER_DURING_HOLD #ifdef DISABLE_LASER_DURING_HOLD
if (bit_istrue(settings.flags, BITFLAG_LASER_MODE)) if (laser_mode->get())
system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP);
#endif #endif
@@ -574,14 +554,14 @@ static void protocol_exec_rt_suspend() {
// current location not exceeding the parking target location, and laser mode disabled. // current location not exceeding the parking target location, and laser mode disabled.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete. // NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
if ((bit_istrue(settings.flags, BITFLAG_HOMING_ENABLE)) && if (homing_enable->get() &&
(parking_target[PARKING_AXIS] < PARKING_TARGET) && (parking_target[PARKING_AXIS] < PARKING_TARGET) &&
bit_isfalse(settings.flags, BITFLAG_LASER_MODE) && laser_mode->get() &&
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) { (sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
#else #else
if ((bit_istrue(settings.flags, BITFLAG_HOMING_ENABLE)) && if (homing_enable->get() &&
(parking_target[PARKING_AXIS] < PARKING_TARGET) && (parking_target[PARKING_AXIS] < PARKING_TARGET) &&
bit_isfalse(settings.flags, BITFLAG_LASER_MODE)) { laser_mode->get()) {
#endif #endif
// Retract spindle by pullout distance. Ensure retraction motion moves away from // Retract spindle by pullout distance. Ensure retraction motion moves away from
// the workpiece and waypoint motion doesn't exceed the parking target location. // the workpiece and waypoint motion doesn't exceed the parking target location.
@@ -634,10 +614,10 @@ static void protocol_exec_rt_suspend() {
// Execute fast restore motion to the pull-out position. Parking requires homing enabled. // Execute fast restore motion to the pull-out position. Parking requires homing enabled.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete. // NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
if (((settings.flags & (BITFLAG_HOMING_ENABLE | BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) && if (homing_enable->get() && !laser_mode->get()) &&
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) { (sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
#else #else
if ((settings.flags & (BITFLAG_HOMING_ENABLE | BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) { if (homing_enable->get() && !laser_mode->get()) {
#endif #endif
// Check to ensure the motion doesn't move below pull-out position. // Check to ensure the motion doesn't move below pull-out position.
if (parking_target[PARKING_AXIS] <= PARKING_TARGET) { if (parking_target[PARKING_AXIS] <= PARKING_TARGET) {
@@ -651,7 +631,7 @@ static void protocol_exec_rt_suspend() {
if (gc_state.modal.spindle != SPINDLE_DISABLE) { if (gc_state.modal.spindle != SPINDLE_DISABLE) {
// Block if safety door re-opened during prior restore actions. // Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) { if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) {
if (bit_istrue(settings.flags, BITFLAG_LASER_MODE)) { if (laser_mode->get()) {
// When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts. // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_RPM); bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_RPM);
} else { } else {
@@ -671,10 +651,10 @@ static void protocol_exec_rt_suspend() {
#ifdef PARKING_ENABLE #ifdef PARKING_ENABLE
// Execute slow plunge motion from pull-out position to resume position. // Execute slow plunge motion from pull-out position to resume position.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL #ifdef ENABLE_PARKING_OVERRIDE_CONTROL
if (((settings.flags & (BITFLAG_HOMING_ENABLE | BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) && if (homing_enable->get() && !laser_mode->get()) &&
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) { (sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
#else #else
if ((settings.flags & (BITFLAG_HOMING_ENABLE | BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) { if (homing_enable->get() && !laser_mode->get()) {
#endif #endif
// Block if safety door re-opened during prior restore actions. // Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) { if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) {
@@ -710,7 +690,7 @@ static void protocol_exec_rt_suspend() {
} else if (sys.spindle_stop_ovr & (SPINDLE_STOP_OVR_RESTORE | SPINDLE_STOP_OVR_RESTORE_CYCLE)) { } else if (sys.spindle_stop_ovr & (SPINDLE_STOP_OVR_RESTORE | SPINDLE_STOP_OVR_RESTORE_CYCLE)) {
if (gc_state.modal.spindle != SPINDLE_DISABLE) { if (gc_state.modal.spindle != SPINDLE_DISABLE) {
report_feedback_message(MESSAGE_SPINDLE_RESTORE); report_feedback_message(MESSAGE_SPINDLE_RESTORE);
if (bit_istrue(settings.flags, BITFLAG_LASER_MODE)) { if (laser_mode->get()) {
// When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts. // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_RPM); bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_RPM);
} else } else

View File

@@ -47,7 +47,9 @@
*/ */
#include "grbl.h" #include "grbl.h"
#ifdef REPORT_HEAP
EspClass esp;
#endif
#define DEFAULTBUFFERSIZE 64 #define DEFAULTBUFFERSIZE 64
// this is a generic send function that everything should use, so interfaces could be added (Bluetooth, etc) // this is a generic send function that everything should use, so interfaces could be added (Bluetooth, etc)
@@ -151,10 +153,10 @@ static void report_util_axis_values(float* axis_value, char* rpt) {
char axisVal[20]; char axisVal[20];
float unit_conv = 1.0; // unit conversion multiplier..default is mm float unit_conv = 1.0; // unit conversion multiplier..default is mm
rpt[0] = '\0'; rpt[0] = '\0';
if (bit_istrue(settings.flags, BITFLAG_REPORT_INCHES)) if (report_inches->get())
unit_conv = 1.0 / MM_PER_INCH; unit_conv = 1.0 / MM_PER_INCH;
for (idx = 0; idx < N_AXIS; idx++) { for (idx = 0; idx < N_AXIS; idx++) {
if (bit_istrue(settings.flags, BITFLAG_REPORT_INCHES)) if (report_inches->get())
sprintf(axisVal, "%4.4f", axis_value[idx] * unit_conv); // Report inches to 4 decimals sprintf(axisVal, "%4.4f", axis_value[idx] * unit_conv); // Report inches to 4 decimals
else else
sprintf(axisVal, "%4.3f", axis_value[idx] * unit_conv); // Report mm to 3 decimals sprintf(axisVal, "%4.3f", axis_value[idx] * unit_conv); // Report mm to 3 decimals
@@ -272,81 +274,10 @@ void report_init_message(uint8_t client) {
// Grbl help message // Grbl help message
void report_grbl_help(uint8_t client) { void report_grbl_help(uint8_t client) {
grbl_send(client, "[HLP:$$ $+ $# $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H $F ~ ! ? ctrl-x]\r\n"); grbl_send(client, "[HLP:$$ $+ $# $S $L $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H $F $E=err ~ ! ? ctrl-x]\r\n");
} }
// Grbl global settings print out.
// NOTE: The numbering scheme here must correlate to storing in settings.c
// Extended setting will be displayed if force_extended is true or #ifdef SHOW_EXTENDED_SETTINGS
void report_grbl_settings(uint8_t client, uint8_t show_extended) {
// Print Grbl settings.
char setting[20];
char rpt[1000];
#ifdef SHOW_EXTENDED_SETTINGS
show_extended = true;
#endif
rpt[0] = '\0';
sprintf(setting, "$0=%d\r\n", settings.pulse_microseconds); strcat(rpt, setting);
sprintf(setting, "$1=%d\r\n", settings.stepper_idle_lock_time); strcat(rpt, setting);
sprintf(setting, "$2=%d\r\n", settings.step_invert_mask); strcat(rpt, setting);
sprintf(setting, "$3=%d\r\n", settings.dir_invert_mask); strcat(rpt, setting);
sprintf(setting, "$4=%d\r\n", bit_istrue(settings.flags, BITFLAG_INVERT_ST_ENABLE)); strcat(rpt, setting);
sprintf(setting, "$5=%d\r\n", bit_istrue(settings.flags, BITFLAG_INVERT_LIMIT_PINS)); strcat(rpt, setting);
sprintf(setting, "$6=%d\r\n", bit_istrue(settings.flags, BITFLAG_INVERT_PROBE_PIN)); strcat(rpt, setting);
sprintf(setting, "$10=%d\r\n", settings.status_report_mask); strcat(rpt, setting);
sprintf(setting, "$11=%4.3f\r\n", settings.junction_deviation); strcat(rpt, setting);
sprintf(setting, "$12=%4.3f\r\n", settings.arc_tolerance); strcat(rpt, setting);
sprintf(setting, "$13=%d\r\n", bit_istrue(settings.flags, BITFLAG_REPORT_INCHES)); strcat(rpt, setting);
sprintf(setting, "$20=%d\r\n", bit_istrue(settings.flags, BITFLAG_SOFT_LIMIT_ENABLE)); strcat(rpt, setting);
sprintf(setting, "$21=%d\r\n", bit_istrue(settings.flags, BITFLAG_HARD_LIMIT_ENABLE)); strcat(rpt, setting);
sprintf(setting, "$22=%d\r\n", bit_istrue(settings.flags, BITFLAG_HOMING_ENABLE)); strcat(rpt, setting);
sprintf(setting, "$23=%d\r\n", settings.homing_dir_mask); strcat(rpt, setting);
sprintf(setting, "$24=%4.3f\r\n", settings.homing_feed_rate); strcat(rpt, setting);
sprintf(setting, "$25=%4.3f\r\n", settings.homing_seek_rate); strcat(rpt, setting);
sprintf(setting, "$26=%d\r\n", settings.homing_debounce_delay); strcat(rpt, setting);
sprintf(setting, "$27=%4.3f\r\n", settings.homing_pulloff); strcat(rpt, setting);
sprintf(setting, "$30=%4.3f\r\n", settings.rpm_max); strcat(rpt, setting);
sprintf(setting, "$31=%4.3f\r\n", settings.rpm_min); strcat(rpt, setting);
sprintf(setting, "$32=%d\r\n", bit_istrue(settings.flags, BITFLAG_LASER_MODE)); strcat(rpt, setting);
if (show_extended) {
sprintf(setting, "$33=%5.3f\r\n", settings.spindle_pwm_freq); strcat(rpt, setting);
sprintf(setting, "$34=%3.3f\r\n", settings.spindle_pwm_off_value); strcat(rpt, setting);
sprintf(setting, "$35=%3.3f\r\n", settings.spindle_pwm_min_value); strcat(rpt, setting);
sprintf(setting, "$36=%3.3f\r\n", settings.spindle_pwm_max_value); strcat(rpt, setting);
for (uint8_t index = 0; index < USER_SETTING_COUNT; index++) {
sprintf(setting, "$%d=%d\r\n", 80 + index, settings.machine_int16[index]); strcat(rpt, setting);
}
for (uint8_t index = 0; index < USER_SETTING_COUNT; index++) {
sprintf(setting, "$%d=%5.3f\r\n", 90 + index, settings.machine_float[index]); strcat(rpt, setting);
}
}
// Print axis settings
uint8_t idx, set_idx;
uint8_t val = AXIS_SETTINGS_START_VAL;
for (set_idx = 0; set_idx < AXIS_N_SETTINGS; set_idx++) {
for (idx = 0; idx < N_AXIS; idx++) {
switch (set_idx) {
case 0: sprintf(setting, "$%d=%4.3f\r\n", val + idx, settings.steps_per_mm[idx]); strcat(rpt, setting); break;
case 1: sprintf(setting, "$%d=%4.3f\r\n", val + idx, settings.max_rate[idx]); strcat(rpt, setting); break;
case 2: sprintf(setting, "$%d=%4.3f\r\n", val + idx, settings.acceleration[idx] / (60 * 60)); strcat(rpt, setting); break;
case 3: sprintf(setting, "$%d=%4.3f\r\n", val + idx, -settings.max_travel[idx]); strcat(rpt, setting); break;
case 4: if (show_extended) {sprintf(setting, "$%d=%4.3f\r\n", val + idx, settings.current[idx]); strcat(rpt, setting);} break;
case 5: if (show_extended) {sprintf(setting, "$%d=%4.3f\r\n", val + idx, settings.hold_current[idx]); strcat(rpt, setting);} break;
case 6: if (show_extended) {sprintf(setting, "$%d=%d\r\n", val + idx, settings.microsteps[idx]); strcat(rpt, setting);} break;
case 7: if (show_extended) {sprintf(setting, "$%d=%d\r\n", val + idx, settings.stallguard[idx]); strcat(rpt, setting);} break;
}
}
val += AXIS_SETTINGS_INCREMENT;
}
grbl_send(client, rpt);
}
// Prints current probe parameters. Upon a probe command, these parameters are updated upon a // Prints current probe parameters. Upon a probe command, these parameters are updated upon a
// successful probe or upon a failed probe with the G38.3 without errors command (if supported). // successful probe or upon a failed probe with the G38.3 without errors command (if supported).
@@ -401,7 +332,7 @@ void report_ngc_parameters(uint8_t client) {
strcat(ngc_rpt, temp); strcat(ngc_rpt, temp);
strcat(ngc_rpt, "]\r\n"); strcat(ngc_rpt, "]\r\n");
strcat(ngc_rpt, "[TLO:"); // Print tool length offset value strcat(ngc_rpt, "[TLO:"); // Print tool length offset value
if (bit_istrue(settings.flags, BITFLAG_REPORT_INCHES)) if (report_inches->get())
sprintf(temp, "%4.3f]\r\n", gc_state.tool_length_offset * INCH_PER_MM); sprintf(temp, "%4.3f]\r\n", gc_state.tool_length_offset * INCH_PER_MM);
else else
sprintf(temp, "%4.3f]\r\n", gc_state.tool_length_offset); sprintf(temp, "%4.3f]\r\n", gc_state.tool_length_offset);
@@ -463,10 +394,7 @@ void report_gcode_modes(uint8_t client) {
sprintf(temp, " T%d", gc_state.tool); sprintf(temp, " T%d", gc_state.tool);
strcat(modes_rpt, temp); strcat(modes_rpt, temp);
if (bit_istrue(settings.flags, BITFLAG_REPORT_INCHES)) sprintf(temp, report_inches->get() ? " F%.1f" : " F%.0f", gc_state.feed_rate);
sprintf(temp, " F%.1f", gc_state.feed_rate);
else
sprintf(temp, " F%.0f", gc_state.feed_rate);
strcat(modes_rpt, temp); strcat(modes_rpt, temp);
sprintf(temp, " S%4.3f", gc_state.spindle_speed); sprintf(temp, " S%4.3f", gc_state.spindle_speed);
strcat(modes_rpt, temp); strcat(modes_rpt, temp);
@@ -477,11 +405,11 @@ void report_gcode_modes(uint8_t client) {
// Prints specified startup line // Prints specified startup line
void report_startup_line(uint8_t n, char* line, uint8_t client) { void report_startup_line(uint8_t n, const char* line, uint8_t client) {
grbl_sendf(client, "$N%d=%s\r\n", n, line); // OK to send to all grbl_sendf(client, "$N%d=%s\r\n", n, line); // OK to send to all
} }
void report_execute_startup_message(char* line, uint8_t status_code, uint8_t client) { void report_execute_startup_message(const char* line, uint8_t status_code, uint8_t client) {
grbl_sendf(client, ">%s:", line); // OK to send to all grbl_sendf(client, ">%s:", line); // OK to send to all
report_status_message(status_code, client); report_status_message(status_code, client);
} }
@@ -558,9 +486,6 @@ void report_build_info(char* line, uint8_t client) {
#endif #endif
} }
// Prints the character string line Grbl has received from the user, which has been pre-parsed, // Prints the character string line Grbl has received from the user, which has been pre-parsed,
// and has been sent into protocol_execute_line() routine to be executed by Grbl. // and has been sent into protocol_execute_line() routine to be executed by Grbl.
void report_echo_line_received(char* line, uint8_t client) { void report_echo_line_received(char* line, uint8_t client) {
@@ -615,18 +540,18 @@ void report_realtime_status(uint8_t client) {
case STATE_SLEEP: strcat(status, "Sleep"); break; case STATE_SLEEP: strcat(status, "Sleep"); break;
} }
float wco[N_AXIS]; float wco[N_AXIS];
if (bit_isfalse(settings.status_report_mask, BITFLAG_RT_STATUS_POSITION_TYPE) || if (bit_isfalse(status_mask->get(), BITFLAG_RT_STATUS_POSITION_TYPE) ||
(sys.report_wco_counter == 0)) { (sys.report_wco_counter == 0)) {
for (idx = 0; idx < N_AXIS; idx++) { for (idx = 0; idx < N_AXIS; idx++) {
// Apply work coordinate offsets and tool length offset to current position. // Apply work coordinate offsets and tool length offset to current position.
wco[idx] = gc_state.coord_system[idx] + gc_state.coord_offset[idx]; wco[idx] = gc_state.coord_system[idx] + gc_state.coord_offset[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) wco[idx] += gc_state.tool_length_offset; if (idx == TOOL_LENGTH_OFFSET_AXIS) wco[idx] += gc_state.tool_length_offset;
if (bit_isfalse(settings.status_report_mask, BITFLAG_RT_STATUS_POSITION_TYPE)) if (bit_isfalse(status_mask->get(), BITFLAG_RT_STATUS_POSITION_TYPE))
print_position[idx] -= wco[idx]; print_position[idx] -= wco[idx];
} }
} }
// Report machine position // Report machine position
if (bit_istrue(settings.status_report_mask, BITFLAG_RT_STATUS_POSITION_TYPE)) if (bit_istrue(status_mask->get(), BITFLAG_RT_STATUS_POSITION_TYPE))
strcat(status, "|MPos:"); strcat(status, "|MPos:");
else { else {
#ifdef USE_FWD_KINEMATIC #ifdef USE_FWD_KINEMATIC
@@ -638,7 +563,7 @@ void report_realtime_status(uint8_t client) {
strcat(status, temp); strcat(status, temp);
// Returns planner and serial read buffer states. // Returns planner and serial read buffer states.
#ifdef REPORT_FIELD_BUFFER_STATE #ifdef REPORT_FIELD_BUFFER_STATE
if (bit_istrue(settings.status_report_mask, BITFLAG_RT_STATUS_BUFFER_STATE)) { if (bit_istrue(status_mask->get(), BITFLAG_RT_STATUS_BUFFER_STATE)) {
int bufsize = DEFAULTBUFFERSIZE; int bufsize = DEFAULTBUFFERSIZE;
#if defined (ENABLE_WIFI) && defined(ENABLE_TELNET) #if defined (ENABLE_WIFI) && defined(ENABLE_TELNET)
if (client == CLIENT_TELNET) if (client == CLIENT_TELNET)
@@ -671,7 +596,7 @@ void report_realtime_status(uint8_t client) {
#endif #endif
// Report realtime feed speed // Report realtime feed speed
#ifdef REPORT_FIELD_CURRENT_FEED_SPEED #ifdef REPORT_FIELD_CURRENT_FEED_SPEED
if (bit_istrue(settings.flags, BITFLAG_REPORT_INCHES)) if (report_inches->get())
sprintf(temp, "|FS:%.1f,%d", st_get_realtime_rate()/ MM_PER_INCH, sys.spindle_speed); sprintf(temp, "|FS:%.1f,%d", st_get_realtime_rate()/ MM_PER_INCH, sys.spindle_speed);
else else
sprintf(temp, "|FS:%.0f,%d", st_get_realtime_rate(), sys.spindle_speed); sprintf(temp, "|FS:%.0f,%d", st_get_realtime_rate(), sys.spindle_speed);
@@ -750,6 +675,10 @@ void report_realtime_status(uint8_t client) {
sd_get_current_filename(temp); sd_get_current_filename(temp);
strcat(status, temp); strcat(status, temp);
} }
#endif
#ifdef REPORT_HEAP
sprintf(temp, "|Heap:%d", esp.getHeapSize());
strcat(status, temp);
#endif #endif
strcat(status, ">\r\n"); strcat(status, ">\r\n");
grbl_send(client, status); grbl_send(client, status);
@@ -817,11 +746,3 @@ char report_get_axis_letter(uint8_t axis) {
return '?'; return '?';
} }
} }
// used to report the pin nhumber or -1 for undefined.
int16_t report_pin_number(uint8_t pin_number) {
if (pin_number == UNDEFINED_PIN)
return -1;
else
return (int16_t)pin_number;
}

View File

@@ -21,8 +21,6 @@
#ifndef report_h #ifndef report_h
#define report_h #define report_h
#include "grbl.h"
// Define Grbl status codes. Valid values (0-255) // Define Grbl status codes. Valid values (0-255)
#define STATUS_OK 0 #define STATUS_OK 0
#define STATUS_EXPECTED_COMMAND_LETTER 1 #define STATUS_EXPECTED_COMMAND_LETTER 1
@@ -69,10 +67,27 @@
#define STATUS_SD_FAILED_OPEN_DIR 62 // SD card failed to open directory #define STATUS_SD_FAILED_OPEN_DIR 62 // SD card failed to open directory
#define STATUS_SD_DIR_NOT_FOUND 63 // SD Card directory not found #define STATUS_SD_DIR_NOT_FOUND 63 // SD Card directory not found
#define STATUS_SD_FILE_EMPTY 64 // SD Card directory not found #define STATUS_SD_FILE_EMPTY 64 // SD Card directory not found
#define STATUS_SD_FILE_NOT_FOUND 65 // SD Card file not found
#define STATUS_SD_FAILED_OPEN_FILE 66 // SD card failed to open file
#define STATUS_SD_FAILED_BUSY 67 // SD card is busy
#define STATUS_SD_FAILED_DEL_DIR 68
#define STATUS_SD_FAILED_DEL_FILE 69
#define STATUS_BT_FAIL_BEGIN 70 // Bluetooth failed to start #define STATUS_BT_FAIL_BEGIN 70 // Bluetooth failed to start
#define STATUS_WIFI_FAIL_BEGIN 71 // WiFi failed to start
#define STATUS_NUMBER_RANGE 80 // Setting number range problem
#define STATUS_INVALID_VALUE 81 // Setting string problem
#define STATUS_MESSAGE_FAILED 90
#define STATUS_NVS_SET_FAILED 100
#define STATUS_AUTHENTICATION_FAILED 110
#define STATUS_EOL 111
typedef uint8_t err_t; // For status codes
const char* errorString(err_t errorNumber);
// Define Grbl alarm codes. Valid values (1-255). 0 is reserved. // Define Grbl alarm codes. Valid values (1-255). 0 is reserved.
#define ALARM_HARD_LIMIT_ERROR EXEC_ALARM_HARD_LIMIT #define ALARM_HARD_LIMIT_ERROR EXEC_ALARM_HARD_LIMIT
@@ -158,8 +173,8 @@ void report_ngc_parameters(uint8_t client);
void report_gcode_modes(uint8_t client); void report_gcode_modes(uint8_t client);
// Prints startup line when requested and executed. // Prints startup line when requested and executed.
void report_startup_line(uint8_t n, char* line, uint8_t client); void report_startup_line(uint8_t n, const char* line, uint8_t client);
void report_execute_startup_message(char* line, uint8_t status_code, uint8_t client); void report_execute_startup_message(const char* line, uint8_t status_code, uint8_t client);
// Prints build info and user info // Prints build info and user info
void report_build_info(char* line, uint8_t client); void report_build_info(char* line, uint8_t client);
@@ -175,6 +190,5 @@ void report_machine_type(uint8_t client);
void report_hex_msg(char* buf, const char *prefix, int len); void report_hex_msg(char* buf, const char *prefix, int len);
char report_get_axis_letter(uint8_t axis); char report_get_axis_letter(uint8_t axis);
int16_t report_pin_number(uint8_t pin_number);
#endif #endif

View File

@@ -56,7 +56,6 @@
*/ */
#include "grbl.h" #include "grbl.h"
#include "commands.h"
portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED; portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED;

View File

@@ -21,8 +21,7 @@
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
//#include "grbl.h" #include "grbl.h"
#include "config.h"
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) #if defined (ENABLE_WIFI) && defined(ENABLE_HTTP)

View File

@@ -267,18 +267,18 @@ void ServoAxis::set_location() {
// 5. Apply the calibrarion // 5. Apply the calibrarion
servo_pulse_min = SERVO_MIN_PULSE; servo_pulse_min = SERVO_MIN_PULSE;
servo_pulse_max = SERVO_MAX_PULSE; servo_pulse_max = SERVO_MAX_PULSE;
if (bit_istrue(settings.dir_invert_mask, bit(_axis))) // this allows the user to change the direction via settings if (bit_istrue(dir_invert_mask->get(), bit(_axis))) // this allows the user to change the direction via settings
swap(servo_pulse_min, servo_pulse_max); swap(servo_pulse_min, servo_pulse_max);
// get the calibration values // get the calibration values
if (_cal_is_valid()) { // if calibration settings are OK then apply them if (_cal_is_valid()) { // if calibration settings are OK then apply them
// apply a calibration // apply a calibration
// the cals apply differently if the direction is reverse (i.e. longer pulse is lower position) // the cals apply differently if the direction is reverse (i.e. longer pulse is lower position)
if (bit_isfalse(settings.dir_invert_mask, bit(_axis))) { // normal direction if (bit_isfalse(dir_invert_mask->get(), bit(_axis))) { // normal direction
min_pulse_cal = 2.0 - (settings.steps_per_mm[_axis] / 100.0); min_pulse_cal = 2.0 - (axis_settings[_axis]->steps_per_mm->get() / 100.0);
max_pulse_cal = (settings.max_travel[_axis] / -100.0); max_pulse_cal = (axis_settings[_axis]->max_travel->get() / 100.0);
} else { // inverted direction } else { // inverted direction
min_pulse_cal = (settings.steps_per_mm[_axis] / 100.0); min_pulse_cal = (axis_settings[_axis]->steps_per_mm->get() / 100.0);
max_pulse_cal = 2.0 - (settings.max_travel[_axis] / -100.0); max_pulse_cal = 2.0 - (axis_settings[_axis]->max_travel->get() / -100.0);
} }
} else { // settings are not valid so don't apply any calibration } else { // settings are not valid so don't apply any calibration
min_pulse_cal = 1.0; min_pulse_cal = 1.0;
@@ -306,27 +306,25 @@ void ServoAxis::disable() {
// vebose = true if you want an error sent to serial port // vebose = true if you want an error sent to serial port
bool ServoAxis::_cal_is_valid() { bool ServoAxis::_cal_is_valid() {
bool settingsOK = true; bool settingsOK = true;
if ((settings.steps_per_mm[_axis] < SERVO_CAL_MIN) || (settings.steps_per_mm[_axis] > SERVO_CAL_MAX)) { if ((axis_settings[_axis]->steps_per_mm->get() < SERVO_CAL_MIN) || (axis_settings[_axis]->steps_per_mm->get() > SERVO_CAL_MAX)) {
if (_showError) { if (_showError) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration ($10%d) value error. Reset to 100", _axis); grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration ($10%d) value error. Reset to 100", _axis);
settings.steps_per_mm[_axis] = 100; char reset_val[] = "100";
write_global_settings(); axis_settings[_axis]->steps_per_mm->setStringValue(reset_val);
} }
settingsOK = false; settingsOK = false;
} }
// Note: Max travel is set positive via $$, but stored as a negative number // Note: Max travel is set positive via $$, but stored as a negative number
if ((settings.max_travel[_axis] < -SERVO_CAL_MAX) || (settings.max_travel[_axis] > -SERVO_CAL_MIN)) { auto travel = -axis_settings[_axis]->max_travel->get();
if ((travel < -SERVO_CAL_MAX) || travel > -SERVO_CAL_MIN) {
if (_showError) { if (_showError) {
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration ($13%d) value error. Reset to 100", _axis); grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Servo calibration ($13%d) value error. Reset to 100", _axis);
settings.max_travel[_axis] = -100; char reset_val[] = "-100"; // stored as a negative
write_global_settings(); axis_settings[_axis]->max_travel->setStringValue(reset_val);
} }
settingsOK = false; settingsOK = false;
} }
_showError = false; // to show error once _showError = false; // to show error once
if (! settingsOK) {
write_global_settings(); // they were changed so write them to
}
return settingsOK; return settingsOK;
} }

View File

@@ -54,32 +54,7 @@
#ifndef servo_axis_h #ifndef servo_axis_h
#define servo_axis_h #define servo_axis_h
#include "Motors/RcServoClass.h"
// this is the pulse range of a the servo. Typical servos are 0.001 to 0.002 seconds
// some servos have a wider range. You can adjust this here or in the calibration feature
#define SERVO_MIN_PULSE_SEC 0.001 // min pulse in seconds
#define SERVO_MAX_PULSE_SEC 0.002 // max pulse in seconds
#define SERVO_POSITION_MIN_DEFAULT 0.0 // mm
#define SERVO_POSITION_MAX_DEFAULT 20.0 // mm
#define SERVO_PULSE_FREQ 50 // 50Hz ...This is a standard analog servo value. Digital ones can repeat faster
#define SERVO_PULSE_RES_BITS 16 // bits of resolution of PWM (16 is max)
#define SERVO_PULSE_RES_COUNT 65535 // see above TODO...do the math here 2^SERVO_PULSE_RES_BITS
#define SERVO_TIME_PER_BIT ((1.0 / (float)SERVO_PULSE_FREQ) / ((float)SERVO_PULSE_RES_COUNT) ) // seconds
#define SERVO_MIN_PULSE (uint16_t)(SERVO_MIN_PULSE_SEC / SERVO_TIME_PER_BIT) // in timer counts
#define SERVO_MAX_PULSE (uint16_t)(SERVO_MAX_PULSE_SEC / SERVO_TIME_PER_BIT) // in timer counts
#define SERVO_PULSE_RANGE (SERVO_MAX_PULSE-SERVO_MIN_PULSE)
#define SERVO_CAL_MIN 20.0 // Percent: the minimum allowable calibration value
#define SERVO_CAL_MAX 180.0 // Percent: the maximum allowable calibration value
#define SERVO_TIMER_INT_FREQ 20.0 // Hz This is the task frequency
#define SERVO_HOMING_OFF 0 // servo is off during homing #define SERVO_HOMING_OFF 0 // servo is off during homing
#define SERVO_HOMING_TARGET 1 // servo is send to a location during homing #define SERVO_HOMING_TARGET 1 // servo is send to a location during homing

View File

@@ -24,185 +24,6 @@
#include "grbl.h" #include "grbl.h"
settings_t settings;
// Method to store startup lines into EEPROM
void settings_store_startup_line(uint8_t n, char* line) {
#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
protocol_buffer_synchronize(); // A startup line may contain a motion and be executing.
#endif
uint32_t addr = n * (LINE_BUFFER_SIZE + 1) + EEPROM_ADDR_STARTUP_BLOCK;
memcpy_to_eeprom_with_checksum(addr, (char*)line, LINE_BUFFER_SIZE);
}
void settings_init() {
EEPROM.begin(EEPROM_SIZE);
if (!read_global_settings()) {
report_status_message(STATUS_SETTING_READ_FAIL, CLIENT_SERIAL);
settings_restore(SETTINGS_RESTORE_ALL); // Force restore all EEPROM data.
report_grbl_settings(CLIENT_SERIAL, false);
}
}
// Method to restore EEPROM-saved Grbl global settings back to defaults.
void settings_restore(uint8_t restore_flag) {
#if defined(ENABLE_BLUETOOTH) || defined(ENABLE_WIFI)
if (restore_flag & SETTINGS_RESTORE_WIFI_SETTINGS) {
#ifdef ENABLE_WIFI
wifi_config.reset_settings();
#endif
#ifdef ENABLE_BLUETOOTH
bt_config.reset_settings();
#endif
}
#endif
if (restore_flag & SETTINGS_RESTORE_DEFAULTS) {
settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS;
settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME;
settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK;
settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK;
settings.status_report_mask = DEFAULT_STATUS_REPORT_MASK;
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
settings.rpm_max = DEFAULT_SPINDLE_RPM_MAX;
settings.rpm_min = DEFAULT_SPINDLE_RPM_MIN;
settings.spindle_pwm_freq = DEFAULT_SPINDLE_FREQ; // $33 Hz (extended set)
settings.spindle_pwm_off_value = DEFAULT_SPINDLE_OFF_VALUE; // $34 Percent (extended set)
settings.spindle_pwm_min_value = DEFAULT_SPINDLE_MIN_VALUE; // $35 Percent (extended set)
settings.spindle_pwm_max_value = DEFAULT_SPINDLE_MAX_VALUE; // $36 Percent (extended set)
settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK;
settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE;
settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE;
settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY;
settings.homing_pulloff = DEFAULT_HOMING_PULLOFF;
settings.flags = 0;
if (DEFAULT_REPORT_INCHES) settings.flags |= BITFLAG_REPORT_INCHES;
if (DEFAULT_LASER_MODE) settings.flags |= BITFLAG_LASER_MODE;
if (DEFAULT_INVERT_ST_ENABLE) settings.flags |= BITFLAG_INVERT_ST_ENABLE;
if (DEFAULT_HARD_LIMIT_ENABLE) settings.flags |= BITFLAG_HARD_LIMIT_ENABLE;
if (DEFAULT_HOMING_ENABLE) settings.flags |= BITFLAG_HOMING_ENABLE;
if (DEFAULT_SOFT_LIMIT_ENABLE) settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
if (DEFAULT_INVERT_LIMIT_PINS) settings.flags |= BITFLAG_INVERT_LIMIT_PINS;
if (DEFAULT_INVERT_PROBE_PIN) settings.flags |= BITFLAG_INVERT_PROBE_PIN;
settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM;
settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM;
settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM;
settings.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE;
settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE;
settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE;
settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION;
settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION;
settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION;
settings.max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL);
settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL);
settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL);
settings.current[X_AXIS] = DEFAULT_X_CURRENT;
settings.current[Y_AXIS] = DEFAULT_Y_CURRENT;
settings.current[Z_AXIS] = DEFAULT_Z_CURRENT;
settings.hold_current[X_AXIS] = DEFAULT_X_HOLD_CURRENT;
settings.hold_current[Y_AXIS] = DEFAULT_Y_HOLD_CURRENT;
settings.hold_current[Z_AXIS] = DEFAULT_Z_HOLD_CURRENT;
settings.microsteps[X_AXIS] = DEFAULT_X_MICROSTEPS;
settings.microsteps[Y_AXIS] = DEFAULT_Y_MICROSTEPS;
settings.microsteps[Z_AXIS] = DEFAULT_Z_MICROSTEPS;
settings.stallguard[X_AXIS] = DEFAULT_X_STALLGUARD;
settings.stallguard[Y_AXIS] = DEFAULT_Y_STALLGUARD;
settings.stallguard[Z_AXIS] = DEFAULT_Z_STALLGUARD;
#if (N_AXIS > A_AXIS)
settings.steps_per_mm[A_AXIS] = DEFAULT_A_STEPS_PER_MM;
settings.max_rate[A_AXIS] = DEFAULT_A_MAX_RATE;
settings.acceleration[A_AXIS] = DEFAULT_A_ACCELERATION;
settings.max_travel[A_AXIS] = (-DEFAULT_A_MAX_TRAVEL);
settings.current[A_AXIS] = DEFAULT_A_CURRENT;
settings.hold_current[A_AXIS] = DEFAULT_A_HOLD_CURRENT;
settings.microsteps[A_AXIS] = DEFAULT_A_MICROSTEPS;
settings.stallguard[A_AXIS] = DEFAULT_Z_STALLGUARD;
#endif
#if (N_AXIS > B_AXIS)
settings.steps_per_mm[B_AXIS] = DEFAULT_B_STEPS_PER_MM;
settings.max_rate[B_AXIS] = DEFAULT_B_MAX_RATE;
settings.acceleration[B_AXIS] = DEFAULT_B_ACCELERATION;
settings.max_travel[B_AXIS] = (-DEFAULT_B_MAX_TRAVEL);
settings.current[B_AXIS] = DEFAULT_B_CURRENT;
settings.hold_current[B_AXIS] = DEFAULT_B_HOLD_CURRENT;
settings.microsteps[B_AXIS] = DEFAULT_B_MICROSTEPS;
settings.stallguard[B_AXIS] = DEFAULT_Z_STALLGUARD;
#endif
#if (N_AXIS > C_AXIS)
settings.steps_per_mm[C_AXIS] = DEFAULT_C_STEPS_PER_MM;
settings.max_rate[C_AXIS] = DEFAULT_C_MAX_RATE;
settings.acceleration[C_AXIS] = DEFAULT_C_ACCELERATION;
settings.max_travel[C_AXIS] = (-DEFAULT_C_MAX_TRAVEL);
settings.current[C_AXIS] = DEFAULT_C_CURRENT;
settings.hold_current[C_AXIS] = DEFAULT_C_HOLD_CURRENT;
settings.microsteps[C_AXIS] = DEFAULT_C_MICROSTEPS;
settings.stallguard[C_AXIS] = DEFAULT_Z_STALLGUARD;
#endif
// TODO figure out a clean way to add actual default values
for (uint8_t index = 0; index < USER_SETTING_COUNT; index++) {
settings.machine_int16[index] = 0;
settings.machine_float[index] = 0.0;
}
// User Integer values
settings.machine_int16[0] = DEFAULT_USER_INT_80;
settings.machine_int16[1] = DEFAULT_USER_INT_81;
settings.machine_int16[2] = DEFAULT_USER_INT_82;
settings.machine_int16[3] = DEFAULT_USER_INT_83;
settings.machine_int16[4] = DEFAULT_USER_INT_84;
// User Integer values
settings.machine_float[0] = DEFAULT_USER_FLOAT_90;
settings.machine_float[1] = DEFAULT_USER_FLOAT_91;
settings.machine_float[2] = DEFAULT_USER_FLOAT_92;
settings.machine_float[3] = DEFAULT_USER_FLOAT_93;
settings.machine_float[4] = DEFAULT_USER_FLOAT_94;
write_global_settings();
}
if (restore_flag & SETTINGS_RESTORE_PARAMETERS) {
uint8_t idx;
float coord_data[N_AXIS];
memset(&coord_data, 0, sizeof(coord_data));
for (idx = 0; idx <= SETTING_INDEX_NCOORD; idx++) settings_write_coord_data(idx, coord_data);
}
if (restore_flag & SETTINGS_RESTORE_STARTUP_LINES) {
#if N_STARTUP_LINE > 0
EEPROM.write(EEPROM_ADDR_STARTUP_BLOCK, 0);
EEPROM.write(EEPROM_ADDR_STARTUP_BLOCK + 1, 0); // Checksum
EEPROM.commit();
#endif
#if N_STARTUP_LINE > 1
EEPROM.write(EEPROM_ADDR_STARTUP_BLOCK + (LINE_BUFFER_SIZE + 1), 0);
EEPROM.write(EEPROM_ADDR_STARTUP_BLOCK + (LINE_BUFFER_SIZE + 2), 0); // Checksum
EEPROM.commit();
#endif
}
if (restore_flag & SETTINGS_RESTORE_BUILD_INFO) {
EEPROM.write(EEPROM_ADDR_BUILD_INFO, 0);
EEPROM.write(EEPROM_ADDR_BUILD_INFO + 1, 0); // Checksum
EEPROM.commit();
}
}
// Reads Grbl global settings struct from EEPROM.
uint8_t read_global_settings() {
// Check version-byte of eeprom
uint8_t version = EEPROM.read(0);
if (version == SETTINGS_VERSION) {
// Read settings-record and check checksum
if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t))))
return (false);
} else
return (false);
return (true);
}
// Method to store Grbl global settings struct and version number into EEPROM
// NOTE: This function can only be called in IDLE state.
void write_global_settings() {
EEPROM.write(0, SETTINGS_VERSION);
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t));
}
// Read selected coordinate data from EEPROM. Updates pointed coord_data value. // Read selected coordinate data from EEPROM. Updates pointed coord_data value.
uint8_t settings_read_coord_data(uint8_t coord_select, float* coord_data) { uint8_t settings_read_coord_data(uint8_t coord_select, float* coord_data) {
@@ -227,7 +48,7 @@ void settings_write_coord_data(uint8_t coord_select, float* coord_data) {
// Method to store build info into EEPROM // Method to store build info into EEPROM
// NOTE: This function can only be called in IDLE state. // NOTE: This function can only be called in IDLE state.
void settings_store_build_info(char* line) { void settings_store_build_info(const char* line) {
// Build info can only be stored when state is IDLE. // Build info can only be stored when state is IDLE.
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO, (char*)line, LINE_BUFFER_SIZE); memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO, (char*)line, LINE_BUFFER_SIZE);
} }
@@ -243,175 +64,15 @@ uint8_t settings_read_build_info(char* line) {
return (true); return (true);
} }
// Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_startup_line(uint8_t n, char* line) {
uint32_t addr = n * (LINE_BUFFER_SIZE + 1) + EEPROM_ADDR_STARTUP_BLOCK;
if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) {
// Reset line with default value
line[0] = 0; // Empty line
settings_store_startup_line(n, line);
return (false);
}
return (true);
}
// A helper method to set settings from command line
uint8_t settings_store_global_setting(uint8_t parameter, float value) {
if (value < 0.0) return (STATUS_NEGATIVE_VALUE);
uint8_t int_value = trunc(value); // integer version
if (parameter >= AXIS_SETTINGS_START_VAL) {
// Store axis configuration. Axis numbering sequence set by AXIS_SETTING defines.
// NOTE: Ensure the setting index corresponds to the report.c settings printout.
parameter -= AXIS_SETTINGS_START_VAL;
uint8_t set_idx = 0;
while (set_idx < AXIS_N_SETTINGS) {
if (parameter < N_AXIS) {
// Valid axis setting found.
switch (set_idx) {
case 0:
#ifdef MAX_STEP_RATE_HZ
if (value * settings.max_rate[parameter] > (MAX_STEP_RATE_HZ * 60.0)) return (STATUS_MAX_STEP_RATE_EXCEEDED);
#endif
settings.steps_per_mm[parameter] = value;
break;
case 1:
#ifdef MAX_STEP_RATE_HZ
if (value * settings.steps_per_mm[parameter] > (MAX_STEP_RATE_HZ * 60.0)) return (STATUS_MAX_STEP_RATE_EXCEEDED);
#endif
settings.max_rate[parameter] = value;
break;
case 2: settings.acceleration[parameter] = value * 60 * 60; break; // Convert to mm/min^2 for grbl internal use.
case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use.
case 4: // run current
settings.current[parameter] = value;
settings_spi_driver_init();
break;
case 5: // hold current
settings.hold_current[parameter] = value;
settings_spi_driver_init();
break;
case 6: // microstepping
settings.microsteps[parameter] = int_value;
settings_spi_driver_init();
break;
case 7: // stallguard
settings.stallguard[parameter] = int_value;
settings_spi_driver_init();
break;
}
break; // Exit while-loop after setting has been configured and proceed to the EEPROM write call.
} else {
set_idx++;
// If axis index greater than N_AXIS or setting index greater than number of axis settings, error out.
if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) return (STATUS_INVALID_STATEMENT);
parameter -= AXIS_SETTINGS_INCREMENT;
}
}
} else {
// Store non-axis Grbl settings
switch (parameter) {
case 0:
if (int_value < 3) return (STATUS_SETTING_STEP_PULSE_MIN);
settings.pulse_microseconds = int_value; break;
case 1: settings.stepper_idle_lock_time = int_value; break;
case 2:
settings.step_invert_mask = int_value;
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks.
break;
case 3:
settings.dir_invert_mask = int_value;
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks.
break;
case 4: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) settings.flags |= BITFLAG_INVERT_ST_ENABLE;
else settings.flags &= ~BITFLAG_INVERT_ST_ENABLE;
break;
case 5: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) settings.flags |= BITFLAG_INVERT_LIMIT_PINS;
else settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS;
break;
case 6: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) settings.flags |= BITFLAG_INVERT_PROBE_PIN;
else settings.flags &= ~BITFLAG_INVERT_PROBE_PIN;
probe_configure_invert_mask(false);
break;
case 10: settings.status_report_mask = int_value; break;
case 11: settings.junction_deviation = value; break;
case 12: settings.arc_tolerance = value; break;
case 13:
if (int_value) settings.flags |= BITFLAG_REPORT_INCHES;
else settings.flags &= ~BITFLAG_REPORT_INCHES;
system_flag_wco_change(); // Make sure WCO is immediately updated.
break;
case 20:
if (int_value) {
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) return (STATUS_SOFT_LIMIT_ERROR);
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
} else settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE;
break;
case 21:
if (int_value) settings.flags |= BITFLAG_HARD_LIMIT_ENABLE;
else settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE;
limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later.
break;
case 22:
if (int_value) settings.flags |= BITFLAG_HOMING_ENABLE;
else {
settings.flags &= ~BITFLAG_HOMING_ENABLE;
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits.
}
break;
case 23: settings.homing_dir_mask = int_value; break;
case 24: settings.homing_feed_rate = value; break;
case 25: settings.homing_seek_rate = value; break;
case 26: settings.homing_debounce_delay = int_value; break;
case 27: settings.homing_pulloff = value; break;
case 30: settings.rpm_max = std::max(value, 1.0f); spindle->init(); break; // Re-initialize spindle rpm calibration (min of 1)
case 31: settings.rpm_min = value; spindle->init(); break; // Re-initialize spindle rpm calibration
case 32:
if (int_value)
settings.flags |= BITFLAG_LASER_MODE;
else
settings.flags &= ~BITFLAG_LASER_MODE;
spindle->init(); // update the spindle class
break;
case 33: settings.spindle_pwm_freq = value; spindle_select(SPINDLE_TYPE); break; // Re-initialize spindle pwm calibration
case 34: settings.spindle_pwm_off_value = value; spindle_select(SPINDLE_TYPE); break; // Re-initialize spindle pwm calibration
case 35: settings.spindle_pwm_min_value = value; spindle_select(SPINDLE_TYPE); break; // Re-initialize spindle pwm calibration
case 36: settings.spindle_pwm_max_value = value; spindle_select(SPINDLE_TYPE); break; // Re-initialize spindle pwm calibration
case 80:
case 81:
case 82:
case 83:
case 84:
settings.machine_int16[parameter - 80] = (uint16_t)value;
break;
case 90:
case 91:
case 92:
case 93:
case 94:
settings.machine_float[parameter - 90] = value;
break;
default:
return (STATUS_INVALID_STATEMENT);
}
}
write_global_settings();
return (STATUS_OK);
}
// Returns step pin mask according to Grbl internal axis indexing. // Returns step pin mask according to Grbl internal axis indexing.
uint8_t get_step_pin_mask(uint8_t axis_idx) { uint8_t get_step_pin_mask(uint8_t axis_idx) {
// todo clean this up further up stream // todo clean this up further up stream
return (1 << axis_idx); return bit(axis_idx);
} }
// Returns direction pin mask according to Grbl internal axis indexing. // Returns direction pin mask according to Grbl internal axis indexing.
uint8_t get_direction_pin_mask(uint8_t axis_idx) { uint8_t get_direction_pin_mask(uint8_t axis_idx) {
return (1 << axis_idx); return bit(axis_idx);
} }
// this allows a conditional re-init of the trinamic settings // this allows a conditional re-init of the trinamic settings
@@ -421,4 +82,4 @@ void settings_spi_driver_init() {
#else #else
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "No SPI drivers setup"); grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "No SPI drivers setup");
#endif #endif
} }

Some files were not shown because too many files have changed in this diff Show More