Update de grbl avec un plus grand buffer et un peu de doc en plus

This commit is contained in:
Geekoid 2019-12-18 21:26:27 +01:00
parent c24dd1bb34
commit dd41d3358a
159 changed files with 38922 additions and 18 deletions

2
Grbl_Esp32-master/.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

View File

@ -0,0 +1,22 @@
---
name: Bug Report
about: Use this template to report bugs.
title: ''
labels: bug
assignees: ''
---
Please answer the following questions.
What version of the firmware are you using?
Is the problem repeatable?
Under what conditions does the bug occur?
**Important** If you paste firmware code, please use [Markdown Code and Syntax Highlighting](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code) with language C++. Use the three back tick method.
```C++
#define EASIER_TO_READ true
```

View File

@ -0,0 +1,16 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Please describe the feature you would like implemented**
**Why do you think this would improve Grbl_ESP32?**
**What do you need the feature for?**
**Will this feature appear to a lot of users?**

View File

@ -0,0 +1,10 @@
---
name: General Discussion
about: A general topic of interest to the group
title: ''
labels: ''
assignees: ''
---

View File

@ -0,0 +1,22 @@
---
name: Problem Compiling Firmware
about: Use this template to submit compiling issues
title: Problems Compiling Firmware
labels: help wanted
assignees: ''
---
Please answer the following questions:
Have you read the [wiki regarding how to compile](https://github.com/bdring/Grbl_Esp32/wiki/Compiling-the-firmware)?
What version of the Arduino IDE are you using?
What version (commit date) of the [Arduino core for the ESP32](https://github.com/espressif/arduino-esp32) are you using?
Are you using the master branch of Grbl_ESP32?
Have you made any edits or configuration changes (list them) to the firmware?
Please paste the compiler error text here:

6
Grbl_Esp32-master/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.pioenvs/
Thumbs.db
.DS_Store
*.orig
embedded/node_modules
embedded/dist

View File

@ -0,0 +1,48 @@
sudo: false
language: bash
os:
- linux
before_script:
- "export DISPLAY=:99.0"
- sleep 3 # give xvfb some time to start
- wget http://downloads.arduino.cc/arduino-1.8.5-linux64.tar.xz
- tar xf arduino-1.8.5-linux64.tar.xz
- mv arduino-1.8.5 $HOME/arduino_ide
- cd $HOME/arduino_ide/hardware
- mkdir esp32
- cd esp32
- git clone https://github.com/espressif/arduino-esp32.git esp32
- cd esp32/tools
- python get.py
- cd ..
- mv $TRAVIS_BUILD_DIR/libraries/ESP32SSDP $HOME/arduino_ide/libraries/
- mv $TRAVIS_BUILD_DIR/libraries/arduinoWebSockets $HOME/arduino_ide/libraries/
script:
- cd $TRAVIS_BUILD_DIR
- source command.sh
- export PATH="$HOME/arduino_ide:$PATH"
- arduino --board esp32:esp32:esp32:PartitionScheme=min_spiffs,FlashFreq=40 --pref compiler.warning_level=all --save-prefs
- sed -n '48,72p;73q' $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- sed -i "s/\/\/#define ENABLE_BLUETOOTH/#define ENABLE_BLUETOOTH/g" $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- sed -i "s/#define ENABLE_BLUETOOTH/\/\/#define ENABLE_BLUETOOTH/g" $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- sed -i "s/\/\/#define ENABLE_WIFI/#define ENABLE_WIFI/g" $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- sed -n '48,72p;73q' $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- build_sketch $TRAVIS_BUILD_DIR/Grbl_Esp32/Grbl_Esp32.ino
- sed -i "s/\/\/#define ENABLE_BLUETOOTH/#define ENABLE_BLUETOOTH/g" $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- sed -i "s/#define ENABLE_WIFI/\/\/#define ENABLE_WIFI/g" $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- sed -n '48,72p;73q' $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- build_sketch $TRAVIS_BUILD_DIR/Grbl_Esp32/Grbl_Esp32.ino
- sed -i "s/\/\/#define ENABLE_BLUETOOTH/#define ENABLE_BLUETOOTH/g" $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- sed -i "s/\/\/#define ENABLE_WIFI/#define ENABLE_WIFI/g" $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- sed -n '48,72p;73q' $TRAVIS_BUILD_DIR/Grbl_Esp32/config.h
- build_sketch $TRAVIS_BUILD_DIR/Grbl_Esp32/Grbl_Esp32.ino
notifications:
email:
on_success: change
on_failure: change

View File

@ -0,0 +1,198 @@
/*
BTconfig.cpp - Bluetooth functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef ARDUINO_ARCH_ESP32
#include "config.h"
#ifdef ENABLE_BLUETOOTH
#include <Preferences.h>
#include "BluetoothSerial.h"
#include "BTconfig.h"
#include "commands.h"
#include "report.h"
BTConfig bt_config;
BluetoothSerial SerialBT;
#ifdef __cplusplus
extern "C" {
#endif
const uint8_t *esp_bt_dev_get_address(void);
#ifdef __cplusplus
}
#endif
String BTConfig::_btname = "";
String BTConfig::_btclient = "";
BTConfig::BTConfig(){
}
BTConfig::~BTConfig(){
end();
}
static void my_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
switch (event)
{
case ESP_SPP_SRV_OPEN_EVT://Server connection open
{
char str[18];
str[17]='\0';
uint8_t * addr = param->srv_open.rem_bda;
sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
BTConfig::_btclient = str;
grbl_sendf(CLIENT_ALL,"[MSG:BT Connected with %s]\r\n", str);
}
break;
case ESP_SPP_CLOSE_EVT://Client connection closed
grbl_send(CLIENT_ALL,"[MSG:BT Disconnected]\r\n");
BTConfig::_btclient="";
break;
default:
break;
}
}
const char *BTConfig::info(){
static String result;
String tmp;
result = "[MSG:";
if(Is_BT_on()) {
result += "Mode=BT:Name=";
result += _btname;
result += "(";
result += device_address();
result += "):Status=";
if (SerialBT.hasClient()){
result += "Connected with " + _btclient;
} else result += "Not connected";
}
else result+="No BT";
result+= "]\r\n";
return result.c_str();
}
/**
* Check if BlueTooth string is valid
*/
bool BTConfig::isBTnameValid (const char * hostname){
//limited size
char c;
if (strlen (hostname) > MAX_BTNAME_LENGTH || strlen (hostname) < MIN_BTNAME_LENGTH) {
return false;
}
//only letter and digit
for (int i = 0; i < strlen (hostname); i++) {
c = hostname[i];
if (! (isdigit (c) || isalpha (c) || c == '_') ) {
return false;
}
}
return true;
}
const char* BTConfig::device_address(){
const uint8_t* point = esp_bt_dev_get_address();
static char str[18];
str[17]='\0';
sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", (int)point[0], (int)point[1], (int)point[2], (int)point[3], (int)point[4], (int)point[5]);
return str;
}
/**
* begin WiFi setup
*/
void BTConfig::begin() {
Preferences prefs;
//stop active services
end();
prefs.begin(NAMESPACE, true);
//Get hostname
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))
{
report_status_message(STATUS_BT_FAIL_BEGIN, CLIENT_ALL);
} else {
SerialBT.register_callback(&my_spp_cb);
grbl_sendf(CLIENT_ALL,"[MSG:BT Started with %s]\r\n", _btname.c_str());
}
}else end();
}
/**
* End WiFi
*/
void BTConfig::end() {
SerialBT.end();
}
/**
* Reset ESP
*/
void BTConfig::reset_settings(){
Preferences prefs;
prefs.begin(NAMESPACE, false);
String sval;
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");
}
}
/**
* Check if BT is on and working
*/
bool BTConfig::Is_BT_on(){
return btStarted();
}
/**
* Handle not critical actions that must be done in sync environement
*/
void BTConfig::handle() {
//If needed
COMMANDS::wait(0);
}
#endif // ENABLE_BLUETOOTH
#endif // ARDUINO_ARCH_ESP32

View File

@ -0,0 +1,65 @@
/*
BTconfig.h - Bluetooth functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
//Preferences entries
#define BT_NAME_ENTRY "BT_NAME"
//defaults values
#define DEFAULT_BT_NAME "btgrblesp"
//boundaries
#define MAX_BTNAME_LENGTH 32
#define MIN_BTNAME_LENGTH 1
#define BT_EVENT_DISCONNECTED 0
#define BT_EVENT_CONNECTED 1
#ifndef _BT_CONFIG_H
#define _BT_CONFIG_H
#include "BluetoothSerial.h"
extern BluetoothSerial SerialBT;
class BTConfig {
public:
BTConfig();
~BTConfig();
static const char *info();
static void BTEvent(uint8_t event);
static bool isBTnameValid (const char * hostname);
static String BTname(){return _btname;}
static const char* device_address();
static void begin();
static void end();
static void handle();
static void reset_settings();
static bool Is_BT_on();
static String _btclient;
private :
static String _btname;
};
extern BTConfig bt_config;
#endif

View File

@ -0,0 +1,131 @@
/*
Grbl_ESP32.ino - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
// Declare system global variable structure
system_t sys;
int32_t sys_position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides.
volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
#ifdef DEBUG
volatile uint8_t sys_rt_exec_debug;
#endif
void setup() {
serial_init(); // Setup serial baud rate and interrupts
settings_init(); // Load Grbl settings from EEPROM
stepper_init(); // Configure stepper pins and interrupt timers
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.
#ifdef USE_PEN_SERVO
servo_init();
#endif
#ifdef USE_SERVO_AXES
init_servos();
#endif
#ifdef USE_PEN_SOLENOID
solenoid_init();
#endif
#ifdef USE_MACHINE_INIT
machine_init(); // user supplied function for special initialization
#endif
// Initialize system state.
#ifdef FORCE_INITIALIZATION_ALARM
// Force Grbl into an ALARM state upon a power-cycle or hard reset.
sys.state = STATE_ALARM;
#else
sys.state = STATE_IDLE;
#endif
// Check for power-up and set system alarm if homing is enabled to force homing cycle
// by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
// startup scripts, but allows access to settings and internal commands. Only a homing
// cycle '$H' or kill alarm locks '$X' will disable the alarm.
// NOTE: The startup script will run after successful completion of the homing cycle, but
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into
// things uncontrollably. Very bad.
#ifdef HOMING_INIT_LOCK
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; }
#endif
#ifdef ENABLE_WIFI
wifi_config.begin();
#endif
#ifdef ENABLE_BLUETOOTH
bt_config.begin();
#endif
inputBuffer.begin();
}
void loop() {
// Reset system variables.
uint8_t prior_state = sys.state;
memset(&sys, 0, sizeof(system_t)); // Clear system struct variable.
sys.state = prior_state;
sys.f_override = DEFAULT_FEED_OVERRIDE; // Set to 100%
sys.r_override = DEFAULT_RAPID_OVERRIDE; // Set to 100%
sys.spindle_speed_ovr = DEFAULT_SPINDLE_SPEED_OVERRIDE; // Set to 100%
memset(sys_probe_position,0,sizeof(sys_probe_position)); // Clear probe position.
sys_probe_state = 0;
sys_rt_exec_state = 0;
sys_rt_exec_alarm = 0;
sys_rt_exec_motion_override = 0;
sys_rt_exec_accessory_override = 0;
// Reset Grbl primary systems.
serial_reset_read_buffer(CLIENT_ALL); // Clear serial read buffer
gc_init(); // Set g-code parser to default state
spindle_init();
coolant_init();
limits_init();
probe_init();
plan_reset(); // Clear block buffer and planner variables
st_reset(); // Clear stepper subsystem variables
// Sync cleared gcode and planner positions to current system position.
plan_sync_position();
gc_sync_position();
// put your main code here, to run repeatedly:
report_init_message(CLIENT_ALL);
// Start Grbl main loop. Processes program inputs and executes them.
protocol_main_loop();
}

View File

@ -0,0 +1,324 @@
/*
atari_1020.cpp
Part of Grbl_ESP32
copyright (c) 2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
--------------------------------------------------------------
This contains all the special features required to control an
Atari 1010 Pen Plotter
*/
#include "grbl.h"
#ifdef ATARI_1020
#define HOMING_PHASE_FULL_APPROACH 0 // move to right end
#define HOMING_PHASE_CHECK 1 // check reed switch
#define HOMING_PHASE_RETRACT 2 // retract
#define HOMING_PHASE_SHORT_APPROACH 3 // retract
static TaskHandle_t solenoidSyncTaskHandle = 0;
static TaskHandle_t atariHomingTaskHandle = 0;
uint16_t solenoid_pull_count;
bool atari_homing = false;
uint8_t homing_phase = HOMING_PHASE_FULL_APPROACH;
uint8_t current_tool;
void machine_init()
{
solenoid_pull_count = 0; // initialize
grbl_send(CLIENT_SERIAL, "[MSG:Atari 1020 Solenoid]\r\n");
// setup PWM channel
ledcSetup(SOLENOID_CHANNEL_NUM, SOLENOID_PWM_FREQ, SOLENOID_PWM_RES_BITS);
ledcAttachPin(SOLENOID_PEN_PIN, SOLENOID_CHANNEL_NUM);
pinMode(SOLENOID_DIRECTION_PIN, OUTPUT); // this sets the direction of the solenoid current
pinMode(REED_SW_PIN, INPUT_PULLUP); // external pullup required
// setup a task that will calculate solenoid position
xTaskCreatePinnedToCore( solenoidSyncTask, // task
"solenoidSyncTask", // name for task
4096, // size of task stack
NULL, // parameters
1, // priority
&solenoidSyncTaskHandle,
0 // core
);
// setup a task that will do the custom homing sequence
xTaskCreatePinnedToCore( atari_home_task, // task
"atari_home_task", // name for task
4096, // size of task stack
NULL, // parameters
1, // priority
&atariHomingTaskHandle,
0 // core
);
}
// this task tracks the Z position and sets the solenoid
void solenoidSyncTask(void *pvParameters)
{
int32_t current_position[N_AXIS]; // copy of current location
float m_pos[N_AXIS]; // machine position in mm
TickType_t xLastWakeTime;
const TickType_t xSolenoidFrequency = SOLENOID_TASK_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
memcpy(current_position,sys_position,sizeof(sys_position)); // get current position in step
system_convert_array_steps_to_mpos(m_pos,current_position); // convert to millimeters
calc_solenoid(m_pos[Z_AXIS]); // calculate kinematics and move the servos
vTaskDelayUntil(&xLastWakeTime, xSolenoidFrequency);
}
}
// to do...have this return a true or false. This could be used by the normal homing feature to
// continue with regular homing after setup
// return true if this completes homing
bool user_defined_homing() {
// create and start a task to do the special homing
homing_phase = HOMING_PHASE_FULL_APPROACH;
atari_homing = true;
return true; // this does it...skip the rest of mc_homing_cycle(...)
}
/*
Do a custom homing routine.
A task is used because it needs to wait until until idle after each move.
1) Do a full travel move to the right. OK to stall if the pen started closer
2) Check for pen 1
3) If fail Retract
4) move to right end
5) Check...
....repeat up to 12 times to try to find pen one
TODO can the retract, move back be 1 phase rather than 2?
*/
void atari_home_task(void *pvParameters) {
uint8_t homing_attempt = 0; // how many times have we tried to home
TickType_t xLastWakeTime;
const TickType_t xHomingTaskFrequency = 100; // in ticks (typically ms) .... need to make sure there is enough time to get out of idle
char gcode_line[20];
while(true) { // this task will only last as long as it is homing
if (atari_homing) {
// must be in idle or alarm state
if (sys.state == STATE_IDLE) {
switch(homing_phase) {
case HOMING_PHASE_FULL_APPROACH: // a full width move to insure it hits left end
inputBuffer.push("G90G0Z1\r"); // lift the pen
sprintf(gcode_line, "G91G0X%3.2f\r", -ATARI_PAPER_WIDTH + ATARI_HOME_POS - 3.0); // plus a little extra
inputBuffer.push(gcode_line);
homing_attempt = 1;
homing_phase = HOMING_PHASE_CHECK;
break;
case HOMING_PHASE_CHECK: // check the limits switch
if (digitalRead(REED_SW_PIN) == 0) { // see if reed switch is grounded
inputBuffer.push("G4P0.1\n"); // dramtic pause
sys_position[X_AXIS] = ATARI_HOME_POS * settings.steps_per_mm[X_AXIS];
sys_position[Y_AXIS] = 0.0;
sys_position[Z_AXIS] = 1.0 * settings.steps_per_mm[Y_AXIS];
gc_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
inputBuffer.push(gcode_line);
current_tool = 1; // local copy for reference...until actual M6 change
gc_state.tool = current_tool;
atari_homing = false; // done with homing sequence
}
else {
homing_phase = HOMING_PHASE_RETRACT;
homing_attempt++;
}
break;
case HOMING_PHASE_RETRACT:
sprintf(gcode_line, "G0X%3.2f\r", -ATARI_HOME_POS);
inputBuffer.push(gcode_line);
sprintf(gcode_line, "G0X%3.2f\r", ATARI_HOME_POS);
inputBuffer.push(gcode_line);
homing_phase = HOMING_PHASE_CHECK;
break;
default:
grbl_sendf(CLIENT_SERIAL, "[MSG:Homing phase error %d]\r\n", homing_phase);
atari_homing = false;; // kills task
break;
}
if (homing_attempt > ATARI_HOMING_ATTEMPTS) { // try all positions plus 1
grbl_send(CLIENT_SERIAL, "[MSG: Atari homing failed]\r\n");
inputBuffer.push("G90\r");
atari_homing = false;;
}
}
}
vTaskDelayUntil(&xLastWakeTime, xHomingTaskFrequency);
}
}
// calculate and set the PWM value for the servo
void calc_solenoid(float penZ)
{
bool isPenUp;
static bool previousPenState = false;
uint32_t solenoid_pen_pulse_len; // duty cycle of solenoid
isPenUp = ( (penZ > 0) || (sys.state == STATE_ALARM) ); // is pen above Z0 or is there an alarm
// if the state has not change, we only count down to the pull time
if (previousPenState == isPenUp) { // if state is unchanged
if (solenoid_pull_count > 0) {
solenoid_pull_count--;
solenoid_pen_pulse_len = SOLENOID_PULSE_LEN_PULL; // stay at full power while counting down
}
else {
solenoid_pen_pulse_len = SOLENOID_PULSE_LEN_HOLD; // pull in delay has expired so lower duty cycle
}
}
else { // pen direction has changed
solenoid_pen_pulse_len = SOLENOID_PULSE_LEN_PULL; // go to full power
solenoid_pull_count = SOLENOID_PULL_DURATION; // set the time to count down
}
previousPenState = isPenUp; // save the prev state
digitalWrite(SOLENOID_DIRECTION_PIN, isPenUp);
// skip setting value if it is unchanged
if (ledcRead(SOLENOID_CHANNEL_NUM) == solenoid_pen_pulse_len)
return;
// update the PWM value
// ledcWrite appears to have issues with interrupts, so make this a critical section
portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&myMutex);
ledcWrite(SOLENOID_CHANNEL_NUM, solenoid_pen_pulse_len);
portEXIT_CRITICAL(&myMutex);
}
/*
A tool (pen) change is done by bumping the carriage against the right edge 3 times per
position change. Pen 1-4 is valid range.
*/
void user_tool_change(uint8_t new_tool) {
uint8_t move_count;
char gcode_line[20];
protocol_buffer_synchronize(); // wait for all previous moves to complete
if ((new_tool < 1) || (new_tool > MAX_PEN_NUMBER)) {
grbl_sendf(CLIENT_ALL, "[MSG: Requested Pen#%d is out of 1-4 range]\r\n", new_tool);
return;
}
if (new_tool == current_tool)
return;
if (new_tool > current_tool) {
move_count = BUMPS_PER_PEN_CHANGE * (new_tool - current_tool);
}
else {
move_count = BUMPS_PER_PEN_CHANGE * ((MAX_PEN_NUMBER - current_tool) + new_tool);
}
sprintf(gcode_line, "G0Z%3.2f\r", ATARI_TOOL_CHANGE_Z); // go to tool change height
inputBuffer.push(gcode_line);
for (uint8_t i = 0; i < move_count; i++) {
sprintf(gcode_line, "G0X%3.2f\r", ATARI_HOME_POS); //
inputBuffer.push(gcode_line);
inputBuffer.push("G0X0\r");
}
current_tool = new_tool;
grbl_sendf(CLIENT_ALL, "[MSG: Change to Pen#%d]\r\n", current_tool);
}
// move from current tool to next tool....
void atari_next_pen() {
if (current_tool < MAX_PEN_NUMBER) {
gc_state.tool = current_tool + 1;
}
else {
gc_state.tool = 1;
}
user_tool_change(gc_state.tool);
}
// Polar coaster has macro buttons, this handles those button pushes.
void user_defined_macro(uint8_t index)
{
char gcode_line[20];
switch (index) {
#ifdef MACRO_BUTTON_0_PIN
case CONTROL_PIN_INDEX_MACRO_0:
grbl_send(CLIENT_SERIAL, "[MSG: Pen Switch]\r\n");
inputBuffer.push("$H\r");
break;
#endif
#ifdef MACRO_BUTTON_1_PIN
case CONTROL_PIN_INDEX_MACRO_1:
grbl_send(CLIENT_SERIAL, "[MSG: Color Switch]\r\n");
atari_next_pen();
sprintf(gcode_line, "G90G0X%3.2f\r", ATARI_PAPER_WIDTH); // alway return to right side to reduce home travel stalls
inputBuffer.push(gcode_line);
break;
#endif
#ifdef MACRO_BUTTON_2_PIN
case CONTROL_PIN_INDEX_MACRO_2:
// feed out some paper and reset the Y 0
grbl_send(CLIENT_SERIAL, "[MSG: Paper Switch]\r\n");
inputBuffer.push("G0Y-25\r");
inputBuffer.push("G4P0.1\r"); // sync...forces wait for planner to clear
sys_position[Y_AXIS] = 0.0; // reset the Y position
gc_sync_position();
plan_sync_position();
break;
#endif
default:
grbl_sendf(CLIENT_SERIAL, "[MSG: Unknown Switch %d]\r\n", index);
break;
}
}
void user_m30() {
char gcode_line[20];
sprintf(gcode_line, "G90G0X%3.2f\r", ATARI_PAPER_WIDTH); //
inputBuffer.push(gcode_line);
}
#endif

View File

@ -0,0 +1,173 @@
/*
atari_1020.h
Part of Grbl_ESP32
copyright (c) 2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
This contains all the special features required to control an
Atari 1010 Pen Plotter
*/
#define CPU_MAP_NAME "CPU_MAP_ATARI_1020"
// ================== CPU MAP ======================
#define USE_UNIPOLAR
#define X_UNIPOLAR
#define X_PIN_PHASE_0 GPIO_NUM_13
#define X_PIN_PHASE_1 GPIO_NUM_21
#define X_PIN_PHASE_2 GPIO_NUM_16
#define X_PIN_PHASE_3 GPIO_NUM_22
#define Y_UNIPOLAR
#define Y_PIN_PHASE_0 GPIO_NUM_25
#define Y_PIN_PHASE_1 GPIO_NUM_27
#define Y_PIN_PHASE_2 GPIO_NUM_26
#define Y_PIN_PHASE_3 GPIO_NUM_32
#define SOLENOID_DIRECTION_PIN GPIO_NUM_4
#define SOLENOID_PEN_PIN GPIO_NUM_2
#define SOLENOID_CHANNEL_NUM 6
#ifdef HOMING_CYCLE_0
#undef HOMING_CYCLE_0
#endif
#define HOMING_CYCLE_0 (1<<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
#define REED_SW_PIN GPIO_NUM_17
#define LIMIT_MASK 0
#ifdef IGNORE_CONTROL_PINS // maybe set in config.h
#undef IGNORE_CONTROL_PINS
#endif
#ifndef ENABLE_CONTROL_SW_DEBOUNCE
#define ENABLE_CONTROL_SW_DEBOUNCE
#endif
#ifdef INVERT_CONTROL_PIN_MASK
#undef IGNORE_CONTROL_PINS
#endif
#define INVERT_CONTROL_PIN_MASK B01110000
#define MACRO_BUTTON_0_PIN GPIO_NUM_34 // Pen Switch
#define MACRO_BUTTON_1_PIN GPIO_NUM_35 // Color Switch
#define MACRO_BUTTON_2_PIN GPIO_NUM_36 // Paper Switch
#ifdef DEFAULTS_GENERIC
#undef DEFAULTS_GENERIC // undefine generic then define each default below
#endif
#define DEFAULT_STEP_PULSE_MICROSECONDS 3
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 200 // 200ms
#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 1
#define DEFAULT_HOMING_DIR_MASK 0
#define DEFAULT_HOMING_FEED_RATE 3000.0 // mm/min
#define DEFAULT_HOMING_SEEK_RATE 3000.0 // mm/min
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)
#define DEFAULT_HOMING_PULLOFF 2.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 10
#define DEFAULT_Y_STEPS_PER_MM 10
#define DEFAULT_Z_STEPS_PER_MM 100.0 // This is percent in servo mode
#define DEFAULT_X_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_X_ACCELERATION (500.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#define DEFAULT_Y_ACCELERATION (500.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#define DEFAULT_Z_ACCELERATION (500.0*60*60)
#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_Z_MAX_TRAVEL 10.0 // This is percent in servo mode
// ================== CPU MAP ======================
#define ATARI_1020
#define SOLENOID_PWM_FREQ 5000
#define SOLENOID_PWM_RES_BITS 8
#define SOLENOID_PULSE_LEN_PULL 255
#define SOLENOID_PULL_DURATION 50 // in task counts...after this delay power will change to hold level see SOLENOID_TASK_FREQ
#define SOLENOID_PULSE_LEN_HOLD 40 // solenoid hold level ... typically a lower value to prevent overheating
#define SOLENOID_TASK_FREQ 50 // this is milliseconds
#define MAX_PEN_NUMBER 4
#define BUMPS_PER_PEN_CHANGE 3
#define ATARI_HOME_POS -10.0f // this amound to the left of the paper 0
#define ATARI_PAPER_WIDTH 100.0f //
#define ATARI_HOMING_ATTEMPTS 13
// tells grbl we have some special functions to call
#define USE_MACHINE_INIT
#define USE_CUSTOM_HOMING
#define USE_TOOL_CHANGE
#define ATARI_TOOL_CHANGE_Z 5.0
#define USE_M30 // use the user defined end of program
#ifndef atari_h
#define atari_h
void machine_init();
void solenoid_disable();
void solenoidSyncTask(void *pvParameters);
void calc_solenoid(float penZ);
bool user_defined_homing();
void atari_home_task(void *pvParameters);
void user_tool_change(uint8_t new_tool);
void user_defined_macro(uint8_t index);
void user_m30();
void atari_next_pen();
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
/*
commands.h - ESP3D configuration class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef COMMANDS_h
#define COMMANDS_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 COMMANDS
{
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 handle();
static void restart_ESP();
#ifdef ENABLE_AUTHENTICATION
static bool isadmin (String & cmd_params);
static bool isuser (String & cmd_params);
static bool isLocalPasswordValid (const char * password);
#endif
private :
static bool restart_ESP_module;
};
#endif

View File

@ -0,0 +1,722 @@
/*
config.h - compile time configuration
Part of Grbl
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
*/
// This file contains compile-time configurations for Grbl's internal system. For the most part,
// users will not need to directly modify these, but they are here for specific needs, i.e.
// performance tuning or adjusting to non-typical machines.
// IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them.
/*
ESP 32 Notes
Some features should not be changed. See notes below.
*/
#ifndef config_h
#define config_h
#include <Arduino.h>
//#define ESP_DEBUG
#define N_AXIS 3 // Number of axes defined (valid range: 3 to 6)
// Define CPU pin map and default settings.
// NOTE: OEMs can avoid the need to maintain/update the defaults.h and cpu_map.h files and use only
// one configuration file by placing their specific defaults and pin map at the bottom of this file.
// If doing so, simply comment out these two defines and see instructions below.
#define CPU_MAP_TEST_DRIVE // these are defined in cpu_map.h
#define VERBOSE_HELP // adds addition help info, but could confuse some senders
// Serial baud rate
#define BAUD_RATE 115200
//#define ENABLE_BLUETOOTH // enable bluetooth ... turns of if $I= something
//#define ENABLE_SD_CARD // enable use of SD Card to run jobs
//#define ENABLE_WIFI //enable wifi
#define ENABLE_HTTP //enable HTTP and all related services
#define ENABLE_OTA //enable OTA
#define ENABLE_TELNET //enable telnet
#define ENABLE_TELNET_WELCOME_MSG //display welcome string when connect to telnet
#define ENABLE_MDNS //enable mDNS discovery
#define ENABLE_SSDP //enable UPNP discovery
#define ENABLE_NOTIFICATIONS //enable notifications
#define ENABLE_SERIAL2SOCKET_IN
#define ENABLE_SERIAL2SOCKET_OUT
#define ENABLE_CAPTIVE_PORTAL
//#define ENABLE_AUTHENTICATION
#define NAMESPACE "GRBL"
#define ESP_RADIO_MODE "RADIO_MODE"
#ifdef ENABLE_AUTHENTICATION
#define DEFAULT_ADMIN_PWD "admin"
#define DEFAULT_USER_PWD "user";
#define DEFAULT_ADMIN_LOGIN "admin"
#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
//Radio Mode
#define ESP_RADIO_OFF 0
#define ESP_WIFI_STA 1
#define ESP_WIFI_AP 2
#define ESP_BT 3
//Default mode
#ifdef ENABLE_WIFI
#define DEFAULT_RADIO_MODE ESP_WIFI_AP
#else
#undef ENABLE_NOTIFICATIONS
#ifdef ENABLE_BLUETOOTH
#define DEFAULT_RADIO_MODE ESP_BT
#else
#define DEFAULT_RADIO_MODE ESP_RADIO_OFF
#endif
#endif
// Define realtime command special characters. These characters are 'picked-off' directly from the
// serial read data stream and are not passed to the grbl line execution parser. Select characters
// that do not and must not exist in the streamed g-code program. ASCII control characters may be
// used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in
// g-code programs, maybe selected for interface programs.
// NOTE: If changed, manually update help message in report.c.
#define CMD_RESET 0x18 // ctrl-x.
#define CMD_STATUS_REPORT '?'
#define CMD_CYCLE_START '~'
#define CMD_FEED_HOLD '!'
// NOTE: All override realtime commands must be in the extended ASCII character set, starting
// at character value 128 (0x80) and up to 255 (0xFF). If the normal set of realtime commands,
// such as status reports, feed hold, reset, and cycle start, are moved to the extended set
// space, serial.c's RX ISR will need to be modified to accommodate the change.
// #define CMD_RESET 0x80
// #define CMD_STATUS_REPORT 0x81
// #define CMD_CYCLE_START 0x82
// #define CMD_FEED_HOLD 0x83
#define CMD_SAFETY_DOOR 0x84
#define CMD_JOG_CANCEL 0x85
#define CMD_DEBUG_REPORT 0x86 // Only when DEBUG enabled, sends debug report in '{}' braces.
#define CMD_FEED_OVR_RESET 0x90 // Restores feed override value to 100%.
#define CMD_FEED_OVR_COARSE_PLUS 0x91
#define CMD_FEED_OVR_COARSE_MINUS 0x92
#define CMD_FEED_OVR_FINE_PLUS 0x93
#define CMD_FEED_OVR_FINE_MINUS 0x94
#define CMD_RAPID_OVR_RESET 0x95 // Restores rapid override value to 100%.
#define CMD_RAPID_OVR_MEDIUM 0x96
#define CMD_RAPID_OVR_LOW 0x97
// #define CMD_RAPID_OVR_EXTRA_LOW 0x98 // *NOT SUPPORTED*
#define CMD_SPINDLE_OVR_RESET 0x99 // Restores spindle override value to 100%.
#define CMD_SPINDLE_OVR_COARSE_PLUS 0x9A
#define CMD_SPINDLE_OVR_COARSE_MINUS 0x9B
#define CMD_SPINDLE_OVR_FINE_PLUS 0x9C
#define CMD_SPINDLE_OVR_FINE_MINUS 0x9D
#define CMD_SPINDLE_OVR_STOP 0x9E
#define CMD_COOLANT_FLOOD_OVR_TOGGLE 0xA0
#define CMD_COOLANT_MIST_OVR_TOGGLE 0xA1
// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces
// the user to perform the homing cycle (or override the locks) before doing anything else. This is
// mainly a safety feature to remind the user to home, since position is unknown to Grbl.
#define HOMING_INIT_LOCK // Comment to disable
// Define the homing cycle patterns with bitmasks. The homing cycle first performs a search mode
// to quickly engage the limit switches, followed by a slower locate mode, and finished by a short
// pull-off motion to disengage the limit switches. The following HOMING_CYCLE_x defines are executed
// in order starting with suffix 0 and completes the homing routine for the specified-axes only. If
// an axis is omitted from the defines, it will not home, nor will the system update its position.
// Meaning that this allows for users with non-standard Cartesian machines, such as a lathe (x then z,
// with no y), to configure the homing cycle behavior to their needs.
// NOTE: The homing cycle is designed to allow sharing of limit pins, if the axes are not in the same
// cycle, but this requires some pin settings changes in cpu_map.h file. For example, the default homing
// cycle can share the Z limit pin with either X or Y limit pins, since they are on different cycles.
// By sharing a pin, this frees up a precious IO pin for other purposes. In theory, all axes limit pins
// may be reduced to one pin, if all axes are homed with separate cycles, or vice versa, all three axes
// on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits
// will not be affected by pin sharing.
// NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y.
#define HOMING_CYCLE_0 (1<<Z_AXIS) // TYPICALLY REQUIRED: First move Z to clear workspace.
#define HOMING_CYCLE_1 (1<<X_AXIS)
#define HOMING_CYCLE_2 (1<<Y_AXIS)
// NOTE: The following is for for homingg X and Y at the same time
// #define HOMING_CYCLE_0 (1<<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!!!
// Number of homing cycles performed after when the machine initially jogs to limit switches.
// This help in preventing overshoot and should improve repeatability. This value should be one or
// greater.
#define N_HOMING_LOCATE_CYCLE 1 // Integer (1-128)
// Enables single axis homing commands. $HX, $HY, and $HZ for X, Y, and Z-axis homing. The full homing
// cycle is still invoked by the $H command. This is disabled by default. It's here only to address
// users that need to switch between a two-axis and three-axis machine. This is actually very rare.
// If you have a two-axis machine, DON'T USE THIS. Instead, just alter the homing cycle for two-axes.
#define HOMING_SINGLE_AXIS_COMMANDS // Default disabled. Uncomment to enable.
// After homing, Grbl will set by default the entire machine space into negative space, as is typical
// for professional CNC machines, regardless of where the limit switches are located. Uncomment this
// define to force Grbl to always set the machine origin at the homed location despite switch orientation.
// #define HOMING_FORCE_SET_ORIGIN // Uncomment to enable.
// Uncomment this define to force Grbl to always set the machine origin at minimum travel positions of
// the axes. Note: The $23 setting determines the direction of travel during homing. If an axes homes towards the
// minimum, it will set the machine position to 0. If it homes towards the maximum it will set the
// machine position to the max travel ($13x), minus the switch pull off ($27).
// #define HOMING_FORCE_POSITIVE_SPACE // Uncomment to enable.
// Number of blocks Grbl executes upon startup. These blocks are stored in EEPROM, where the size
// and addresses are defined in settings.h. With the current settings, up to 2 startup blocks may
// be stored and executed in order. These startup blocks would typically be used to set the g-code
// parser state depending on user preferences.
#define N_STARTUP_LINE 2 // Integer (1-2)
// Number of floating decimal points printed by Grbl for certain value types. These settings are
// determined by realistic and commonly observed values in CNC machines. For example, position
// values cannot be less than 0.001mm or 0.0001in, because machines can not be physically more
// precise this. So, there is likely no need to change these, but you can if you need to here.
// NOTE: Must be an integer value from 0 to ~4. More than 4 may exhibit round-off errors.
// ESP32 Note: These are mostly hard coded, so these values will not change anything
#define N_DECIMAL_COORDVALUE_INCH 4 // Coordinate or position value in inches
#define N_DECIMAL_COORDVALUE_MM 3 // Coordinate or position value in mm
#define N_DECIMAL_RATEVALUE_INCH 1 // Rate or velocity value in in/min
#define N_DECIMAL_RATEVALUE_MM 0 // Rate or velocity value in mm/min
#define N_DECIMAL_SETTINGVALUE 3 // Decimals for floating point setting values
#define N_DECIMAL_RPMVALUE 0 // RPM value in rotations per min.
// If your machine has two limits switches wired in parallel to one axis, you will need to enable
// this feature. Since the two switches are sharing a single pin, there is no way for Grbl to tell
// which one is enabled. This option only effects homing, where if a limit is engaged, Grbl will
// alarm out and force the user to manually disengage the limit switch. Otherwise, if you have one
// limit switch for each axis, don't enable this option. By keeping it disabled, you can perform a
// homing cycle while on the limit switch and not have to move the machine off of it.
// #define LIMITS_TWO_SWITCHES_ON_AXES
// Allows GRBL to track and report gcode line numbers. Enabling this means that the planning buffer
// goes from 16 to 15 to make room for the additional line number data in the plan_block_t struct
// #define USE_LINE_NUMBERS // Disabled by default. Uncomment to enable.
// Upon a successful probe cycle, this option provides immediately feedback of the probe coordinates
// through an automatically generated message. If disabled, users can still access the last probe
// coordinates through Grbl '$#' print parameters.
#define MESSAGE_PROBE_COORDINATES // Enabled by default. Comment to disable.
// Enables a second coolant control pin via the mist coolant g-code command M7 on the Arduino Uno
// analog pin 4. Only use this option if you require a second coolant control pin.
// NOTE: The M8 flood coolant control pin on analog pin 3 will still be functional regardless.
// ESP32 NOTE! This is here for reference only. You enable both M7 and M8 by assigning them a GPIO Pin
// in cpu_map.h
//#define ENABLE_M7 // Don't uncomment...see above!
// This option causes the feed hold input to act as a safety door switch. A safety door, when triggered,
// immediately forces a feed hold and then safely de-energizes the machine. Resuming is blocked until
// the safety door is re-engaged. When it is, Grbl will re-energize the machine and then resume on the
// previous tool path, as if nothing happened.
#define ENABLE_SAFETY_DOOR_INPUT_PIN // ESP32 Leave this enabled for now .. code for undefined not ready
// After the safety door switch has been toggled and restored, this setting sets the power-up delay
// between restoring the spindle and coolant and resuming the cycle.
#define SAFETY_DOOR_SPINDLE_DELAY 4.0 // Float (seconds)
#define SAFETY_DOOR_COOLANT_DELAY 1.0 // Float (seconds)
// Enable CoreXY kinematics. Use ONLY with CoreXY machines.
// 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)
// 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
// described, if not, motions may move in strange directions. Grbl requires the CoreXY A and B motors
// have the same steps per mm internally.
// #define COREXY // Default disabled. Uncomment to enable.
// Enable using a servo for the Z axis on a pen type machine.
// You typically should not define a pin for the Z axis in cpu_map.h
// You should configure your settings in servo_pen.h
// #define USE_PEN_SERVO // this method will be deprecated soon
// #define USE_SERVO_AXES // the new method
// define your servo pin here or in cpu_map.h
//#define SERVO_PEN_PIN GPIO_NUM_27
// Enable using a solenoid for the Z axis on a pen type machine
// #define USE_PEN_SOLENOID
// 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.
// The mask order is Cycle Start | Feed Hold | Reset | Safety Door
// For example B1101 will invert the function of the Reset pin.
#define INVERT_CONTROL_PIN_MASK B1111
// This allows control pins to be ignored.
// Since these are typically used on the pins that don't have pullups, they will float and cause
// problems if not externally pulled up. Ignoring will always return not activated when read.
//#define IGNORE_CONTROL_PINS
#define ENABLE_CONTROL_SW_DEBOUNCE // Default disabled. Uncomment to enable.
#define CONTROL_SW_DEBOUNCE_PERIOD 32 // in milliseconds default 32 microseconds
// Inverts select limit pin states based on the following mask. This effects all limit pin functions,
// such as hard limits and homing. However, this is different from overall invert limits setting.
// This build option will invert only the limit pins defined here, and then the invert limits setting
// 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.
// 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.
// Inverts the spindle enable pin from low-disabled/high-enabled to low-enabled/high-disabled. Useful
// for some pre-built electronic boards.
#define INVERT_SPINDLE_ENABLE_PIN // Default disabled. Uncomment to enable.
// Inverts the selected coolant pin from low-disabled/high-enabled to low-enabled/high-disabled. Useful
// for some pre-built electronic boards.
// #define INVERT_COOLANT_FLOOD_PIN // Default disabled. Uncomment to enable.
// #define INVERT_COOLANT_MIST_PIN // Default disabled. Note: Enable M7 mist coolant in config.h
// When Grbl powers-cycles or is hard reset with the Arduino reset button, Grbl boots up with no ALARM
// by default. This is to make it as simple as possible for new users to start using Grbl. When homing
// is enabled and a user has installed limit switches, Grbl will boot up in an ALARM state to indicate
// Grbl doesn't know its position and to force the user to home before proceeding. This option forces
// Grbl to always initialize into an ALARM state regardless of homing or not. This option is more for
// OEMs and LinuxCNC users that would like this power-cycle behavior.
// #define FORCE_INITIALIZATION_ALARM // Default disabled. Uncomment to enable.
// At power-up or a reset, Grbl will check the limit switch states to ensure they are not active
// before initialization. If it detects a problem and the hard limits setting is enabled, Grbl will
// simply message the user to check the limits and enter an alarm state, rather than idle. Grbl will
// not throw an alarm message.
#define CHECK_LIMITS_AT_INIT
// ---------------------------------------------------------------------------------------
// ADVANCED CONFIGURATION OPTIONS:
// Enables code for debugging purposes. Not for general use and always in constant flux.
// #define DEBUG // Uncomment to enable. Default disabled.
// Configure rapid, feed, and spindle override settings. These values define the max and min
// allowable override values and the coarse and fine increments per command received. Please
// note the allowable values in the descriptions following each define.
#define DEFAULT_FEED_OVERRIDE 100 // 100%. Don't change this value.
#define MAX_FEED_RATE_OVERRIDE 200 // Percent of programmed feed rate (100-255). Usually 120% or 200%
#define MIN_FEED_RATE_OVERRIDE 10 // Percent of programmed feed rate (1-100). Usually 50% or 1%
#define FEED_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%.
#define FEED_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%.
#define DEFAULT_RAPID_OVERRIDE 100 // 100%. Don't change this value.
#define RAPID_OVERRIDE_MEDIUM 50 // Percent of rapid (1-99). Usually 50%.
#define RAPID_OVERRIDE_LOW 25 // Percent of rapid (1-99). Usually 25%.
// #define RAPID_OVERRIDE_EXTRA_LOW 5 // *NOT SUPPORTED* Percent of rapid (1-99). Usually 5%.
#define DEFAULT_SPINDLE_SPEED_OVERRIDE 100 // 100%. Don't change this value.
#define MAX_SPINDLE_SPEED_OVERRIDE 200 // Percent of programmed spindle speed (100-255). Usually 200%.
#define MIN_SPINDLE_SPEED_OVERRIDE 10 // Percent of programmed spindle speed (1-100). Usually 10%.
#define SPINDLE_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%.
#define SPINDLE_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%.
// When a M2 or M30 program end command is executed, most g-code states are restored to their defaults.
// This compile-time option includes the restoring of the feed, rapid, and spindle speed override values
// to their default values at program end.
#define RESTORE_OVERRIDES_AFTER_PROGRAM_END // Default enabled. Comment to disable.
// The status report change for Grbl v1.1 and after also removed the ability to disable/enable most data
// fields from the report. This caused issues for GUI developers, who've had to manage several scenarios
// and configurations. The increased efficiency of the new reporting style allows for all data fields to
// be sent without potential performance issues.
// NOTE: The options below are here only provide a way to disable certain data fields if a unique
// situation demands it, but be aware GUIs may depend on this data. If disabled, it may not be compatible.
#define REPORT_FIELD_BUFFER_STATE // Default enabled. Comment to disable.
#define REPORT_FIELD_PIN_STATE // Default enabled. Comment to disable.
#define REPORT_FIELD_CURRENT_FEED_SPEED // Default enabled. Comment to disable.
#define REPORT_FIELD_WORK_COORD_OFFSET // Default enabled. Comment to disable.
#define REPORT_FIELD_OVERRIDES // Default enabled. Comment to disable.
#define REPORT_FIELD_LINE_NUMBERS // Default enabled. Comment to disable.
// Some status report data isn't necessary for realtime, only intermittently, because the values don't
// change often. The following macros configures how many times a status report needs to be called before
// the associated data is refreshed and included in the status report. However, if one of these value
// changes, Grbl will automatically include this data in the next status report, regardless of what the
// count is at the time. This helps reduce the communication overhead involved with high frequency reporting
// and agressive streaming. There is also a busy and an idle refresh count, which sets up Grbl to send
// refreshes more often when its not doing anything important. With a good GUI, this data doesn't need
// to be refreshed very often, on the order of a several seconds.
// NOTE: WCO refresh must be 2 or greater. OVR refresh must be 1 or greater.
#define REPORT_OVR_REFRESH_BUSY_COUNT 20 // (1-255)
#define REPORT_OVR_REFRESH_IDLE_COUNT 10 // (1-255) Must be less than or equal to the busy count
#define REPORT_WCO_REFRESH_BUSY_COUNT 30 // (2-255)
#define REPORT_WCO_REFRESH_IDLE_COUNT 10 // (2-255) Must be less than or equal to the busy count
// The temporal resolution of the acceleration management subsystem. A higher number gives smoother
// acceleration, particularly noticeable on machines that run at very high feedrates, but may negatively
// impact performance. The correct value for this parameter is machine dependent, so it's advised to
// set this only as high as needed. Approximate successful values can widely range from 50 to 200 or more.
// NOTE: Changing this value also changes the execution time of a segment in the step segment buffer.
// When increasing this value, this stores less overall time in the segment buffer and vice versa. Make
// certain the step segment buffer is increased/decreased to account for these changes.
#define ACCELERATION_TICKS_PER_SECOND 100
// Adaptive Multi-Axis Step Smoothing (AMASS) is an advanced feature that does what its name implies,
// smoothing the stepping of multi-axis motions. This feature smooths motion particularly at low step
// frequencies below 10kHz, where the aliasing between axes of multi-axis motions can cause audible
// noise and shake your machine. At even lower step frequencies, AMASS adapts and provides even better
// step smoothing. See stepper.c for more details on the AMASS system works.
#define ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // Default enabled. Comment to disable.
// Sets the maximum step rate allowed to be written as a Grbl setting. This option enables an error
// check in the settings module to prevent settings values that will exceed this limitation. The maximum
// step rate is strictly limited by the CPU speed and will change if something other than an AVR running
// at 16MHz is used.
// NOTE: For now disabled, will enable if flash space permits.
// #define MAX_STEP_RATE_HZ 30000 // Hz
// By default, Grbl sets all input pins to normal-high operation with their internal pull-up resistors
// enabled. This simplifies the wiring for users by requiring only a switch connected to ground,
// although its recommended that users take the extra step of wiring in low-pass filter to reduce
// electrical noise detected by the pin. If the user inverts the pin in Grbl settings, this just flips
// which high or low reading indicates an active signal. In normal operation, this means the user
// needs to connect a normal-open switch, but if inverted, this means the user should connect a
// normal-closed switch.
// The following options disable the internal pull-up resistors, sets the pins to a normal-low
// operation, and switches must be now connect to Vcc instead of ground. This also flips the meaning
// of the invert pin Grbl setting, where an inverted setting now means the user should connect a
// normal-open switch and vice versa.
// NOTE: All pins associated with the feature are disabled, i.e. XYZ limit pins, not individual axes.
// WARNING: When the pull-ups are disabled, this requires additional wiring with pull-down resistors!
//#define DISABLE_LIMIT_PIN_PULL_UP
//#define DISABLE_PROBE_PIN_PULL_UP
//#define DISABLE_CONTROL_PIN_PULL_UP
// Sets which axis the tool length offset is applied. Assumes the spindle is always parallel with
// the selected axis with the tool oriented toward the negative direction. In other words, a positive
// tool length offset value is subtracted from the current location.
#define TOOL_LENGTH_OFFSET_AXIS Z_AXIS // Default z-axis. Valid values are X_AXIS, Y_AXIS, or Z_AXIS.
// Enables variable spindle output voltage for different RPM values. On the Arduino Uno, the spindle
// enable pin will output 5V for maximum RPM with 256 intermediate levels and 0V when disabled.
// NOTE: IMPORTANT for Arduino Unos! When enabled, the Z-limit pin D11 and spindle enable pin D12 switch!
// The hardware PWM output on pin D11 is required for variable spindle output voltages.
#define VARIABLE_SPINDLE // Default enabled. Comment to disable.
// Alters the behavior of the spindle enable pin. By default Grbl will not disable the enable pin if
// spindle speed is zero and M3/4 is active, but still sets the PWM output to zero. This allows the users
// to know if the spindle is active and use it as an additional control input.
// However, in some use cases, user may want the enable pin to disable with a zero spindle speed and
// re-enable when spindle speed is greater than zero. This option does that.
#define SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED // Default enabled. Comment to disable.
// With this enabled, Grbl sends back an echo of the line it has received, which has been pre-parsed (spaces
// 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
// sendss the line 'g1 x1.032 y2.45 (test comment)', Grbl will echo back in the form '[echo: G1X1.032Y2.45]'.
// 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
// to help minimize transmission waiting within the serial write protocol.
// #define REPORT_ECHO_LINE_RECEIVED // Default disabled. Uncomment to enable.
// 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
// zero. This value controls how fast the machine moves through junctions with no regard for acceleration
// limits or angle between neighboring block line move directions. This is useful for machines that can't
// tolerate the tool dwelling for a split second, i.e. 3d printers or laser cutters. If used, this value
// should not be much greater than zero or to the minimum value necessary for the machine to work.
#define MINIMUM_JUNCTION_SPEED 0.0 // (mm/min)
// Sets the minimum feed rate the planner will allow. Any value below it will be set to this minimum
// value. This also ensures that a planned motion always completes and accounts for any floating-point
// round-off errors. Although not recommended, a lower value than 1.0 mm/min will likely work in smaller
// machines, perhaps to 0.1mm/min, but your success may vary based on multiple factors.
#define MINIMUM_FEED_RATE 1.0 // (mm/min)
// Number of arc generation iterations by small angle approximation before exact arc trajectory
// correction with expensive sin() and cos() calcualtions. This parameter maybe decreased if there
// are issues with the accuracy of the arc generations, or increased if arc execution is getting
// bogged down by too many trig calculations.
#define N_ARC_CORRECTION 12 // Integer (1-255)
// The arc G2/3 g-code standard is problematic by definition. Radius-based arcs have horrible numerical
// errors when arc at semi-circles(pi) or full-circles(2*pi). Offset-based arcs are much more accurate
// but still have a problem when arcs are full-circles (2*pi). This define accounts for the floating
// point issues when offset-based arcs are commanded as full circles, but get interpreted as extremely
// small arcs with around machine epsilon (1.2e-7rad) due to numerical round-off and precision issues.
// This define value sets the machine epsilon cutoff to determine if the arc is a full-circle or not.
// NOTE: Be very careful when adjusting this value. It should always be greater than 1.2e-7 but not too
// much greater than this. The default setting should capture most, if not all, full arc error situations.
#define ARC_ANGULAR_TRAVEL_EPSILON 5E-7 // Float (radians)
// Time delay increments performed during a dwell. The default value is set at 50ms, which provides
// a maximum time delay of roughly 55 minutes, more than enough for most any application. Increasing
// this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of
// run-time command executions, like status reports, since these are performed between each dwell
// time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays.
#define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds)
// For test use only. This uses the ESP32's RMT perifieral to generate step pulses
// It allows the use of the STEP_PULSE_DELAY (see below) and it automatically ends the
// pulse in one operation.
// Dir Pin ____|--------------------
// Step Pin _______|--|____________
// While this is experimental, it is intended to be the future default method after testing
//#define USE_RMT_STEPS
// Creates a delay between the direction pin setting and corresponding step pulse by creating
// another interrupt (Timer2 compare) to manage it. The main Grbl interrupt (Timer1 compare)
// sets the direction pins, and does not immediately set the stepper pins, as it would in
// normal operation. The Timer2 compare fires next to set the stepper pins after the step
// pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed
// by the step pulse time plus the step pulse delay. (Thanks langwadt for the idea!)
// NOTE: Uncomment to enable. The recommended delay must be > 3us, and, when added with the
// user-supplied step pulse time, the total time must not exceed 127us. Reported successful
// values for certain setups have ranged from 5 to 20us.
// must use #define USE_RMT_STEPS for this to work
//#define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled.
// The number of linear motions in the planner buffer to be planned at any give time. The vast
// majority of RAM that Grbl uses is based on this buffer size. Only increase if there is extra
// available RAM, like when re-compiling for a Mega2560. Or decrease if the Arduino begins to
// crash due to the lack of available RAM or if the CPU is having trouble keeping up with planning
// new incoming motions as they are executed.
#define BLOCK_BUFFER_SIZE 32 // Uncomment to override default in planner.h.
// Governs the size of the intermediary step segment buffer between the step execution algorithm
// and the planner blocks. Each segment is set of steps executed at a constant velocity over a
// fixed time defined by ACCELERATION_TICKS_PER_SECOND. They are computed such that the planner
// block velocity profile is traced exactly. The size of this buffer governs how much step
// execution lead time there is for other Grbl processes have to compute and do their thing
// before having to come back and refill this buffer, currently at ~50msec of step moves.
// #define SEGMENT_BUFFER_SIZE 6 // Uncomment to override default in stepper.h.
// Line buffer size from the serial input stream to be executed. Also, governs the size of
// each of the startup blocks, as they are each stored as a string of this size. Make sure
// to account for the available EEPROM at the defined memory address in settings.h and for
// the number of desired startup blocks.
// NOTE: 80 characters is not a problem except for extreme cases, but the line buffer size
// can be too small and g-code blocks can get truncated. Officially, the g-code standards
// support up to 256 characters. In future versions, this default will be increased, when
// we know how much extra memory space we can re-invest into this.
// #define LINE_BUFFER_SIZE 80 // Uncomment to override default in protocol.h
// Serial send and receive buffer size. The receive buffer is often used as another streaming
// buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming
// interfaces will character count and track each block send to each block response. So,
// increase the receive buffer if a deeper receive buffer is needed for streaming and avaiable
// memory allows. The send buffer primarily handles messages in Grbl. Only increase if large
// messages are sent and Grbl begins to stall, waiting to send the rest of the message.
// NOTE: Grbl generates an average status report in about 0.5msec, but the serial TX stream at
// 115200 baud will take 5 msec to transmit a typical 55 character report. Worst case reports are
// around 90-100 characters. As long as the serial TX buffer doesn't get continually maxed, Grbl
// will continue operating efficiently. Size the TX buffer around the size of a worst-case report.
#define RX_BUFFER_SIZE 254 // (1-254) Uncomment to override defaults in serial.h
#define TX_BUFFER_SIZE 128 // (1-254)
// A simple software debouncing feature for hard limit switches. When enabled, the limit
// switch interrupt unblock a waiting task which will recheck the limit switch pins after
// a short delay. Default disabled
#define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable.
#define DEBOUNCE_PERIOD 32 // in milliseconds default 32 microseconds
// Configures the position after a probing cycle during Grbl's check mode. Disabled sets
// the position to the probe target, when enabled sets the position to the start position.
// #define SET_CHECK_MODE_PROBE_TO_START // Default disabled. Uncomment to enable.
// Force Grbl to check the state of the hard limit switches when the processor detects a pin
// change inside the hard limit ISR routine. By default, Grbl will trigger the hard limits
// alarm upon any pin change, since bouncing switches can cause a state check like this to
// misread the pin. When hard limits are triggered, they should be 100% reliable, which is the
// reason that this option is disabled by default. Only if your system/electronics can guarantee
// that the switches don't bounce, we recommend enabling this option. This will help prevent
// triggering a hard limit when the machine disengages from the switch.
// NOTE: This option has no effect if SOFTWARE_DEBOUNCE is enabled.
// #define HARD_LIMIT_FORCE_STATE_CHECK // Default disabled. Uncomment to enable.
// Adjusts homing cycle search and locate scalars. These are the multipliers used by Grbl's
// homing cycle to ensure the limit switches are engaged and cleared through each phase of
// the cycle. The search phase uses the axes max-travel setting times the SEARCH_SCALAR to
// determine distance to look for the limit switch. Once found, the locate phase begins and
// uses the homing pull-off distance setting times the LOCATE_SCALAR to pull-off and re-engage
// the limit switch.
// NOTE: Both of these values must be greater than 1.0 to ensure proper function.
// #define HOMING_AXIS_SEARCH_SCALAR 1.5 // Uncomment to override defaults in limits.c.
// #define HOMING_AXIS_LOCATE_SCALAR 10.0 // Uncomment to override defaults in limits.c.
// Enable the '$RST=*', '$RST=$', and '$RST=#' eeprom restore commands. There are cases where
// these commands may be undesirable. Simply comment the desired macro to disable it.
// NOTE: See SETTINGS_RESTORE_ALL macro for customizing the `$RST=*` command.
#define ENABLE_RESTORE_EEPROM_WIPE_ALL // '$RST=*' Default enabled. Comment to disable.
#define ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS // '$RST=$' Default enabled. Comment to disable.
#define ENABLE_RESTORE_EEPROM_CLEAR_PARAMETERS // '$RST=#' Default enabled. Comment to disable.
// Defines the EEPROM data restored upon a settings version change and `$RST=*` command. Whenever the
// the settings or other EEPROM data structure changes between Grbl versions, Grbl will automatically
// wipe and restore the EEPROM. This macro controls what data is wiped and restored. This is useful
// particularily for OEMs that need to retain certain data. For example, the BUILD_INFO string can be
// written into the Arduino EEPROM via a seperate .INO sketch to contain product data. Altering this
// macro to not restore the build info EEPROM will ensure this data is retained after firmware upgrades.
// NOTE: Uncomment to override defaults in settings.h
// #define SETTINGS_RESTORE_ALL (SETTINGS_RESTORE_DEFAULTS | SETTINGS_RESTORE_PARAMETERS | SETTINGS_RESTORE_STARTUP_LINES | SETTINGS_RESTORE_BUILD_INFO)
// Additional settings have been added to the original set that you see with the $$ command
// Some senders may not be able to parse anything different from the original set
// You can still set these like $33=5000, but you cannot read them back.
// Default is off to limit support issues...you can enable here or in your cpu_map
// #define SHOW_EXTENDED_SETTINGS
// Enable the '$I=(string)' build info write command. If disabled, any existing build info data must
// be placed into EEPROM via external means with a valid checksum value. This macro option is useful
// to prevent this data from being over-written by a user, when used to store OEM product data.
// NOTE: If disabled and to ensure Grbl can never alter the build info line, you'll also need to enable
// the SETTING_RESTORE_ALL macro above and remove SETTINGS_RESTORE_BUILD_INFO from the mask.
// NOTE: See the included grblWrite_BuildInfo.ino example file to write this string seperately.
#define ENABLE_BUILD_INFO_WRITE_COMMAND // '$I=' Default enabled. Comment to disable.
// AVR processors require all interrupts to be disabled during an EEPROM write. This includes both
// the stepper ISRs and serial comm ISRs. In the event of a long EEPROM write, this ISR pause can
// cause active stepping to lose position and serial receive data to be lost. This configuration
// option forces the planner buffer to completely empty whenever the EEPROM is written to prevent
// any chance of lost steps.
// However, this doesn't prevent issues with lost serial RX data during an EEPROM write, especially
// if a GUI is premptively filling up the serial RX buffer simultaneously. It's highly advised for
// GUIs to flag these gcodes (G10,G28.1,G30.1) to always wait for an 'ok' after a block containing
// one of these commands before sending more data to eliminate this issue.
// NOTE: Most EEPROM write commands are implicitly blocked during a job (all '$' commands). However,
// coordinate set g-code commands (G10,G28/30.1) are not, since they are part of an active streaming
// job. At this time, this option only forces a planner buffer sync with these g-code commands.
#define FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE // Default enabled. Comment to disable.
// In Grbl v0.9 and prior, there is an old outstanding bug where the `WPos:` work position reported
// may not correlate to what is executing, because `WPos:` is based on the g-code parser state, which
// can be several motions behind. This option forces the planner buffer to empty, sync, and stop
// motion whenever there is a command that alters the work coordinate offsets `G10,G43.1,G92,G54-59`.
// This is the simplest way to ensure `WPos:` is always correct. Fortunately, it's exceedingly rare
// that any of these commands are used need continuous motions through them.
#define FORCE_BUFFER_SYNC_DURING_WCO_CHANGE // Default enabled. Comment to disable.
// By default, Grbl disables feed rate overrides for all G38.x probe cycle commands. Although this
// may be different than some pro-class machine control, it's arguable that it should be this way.
// Most probe sensors produce different levels of error that is dependent on rate of speed. By
// keeping probing cycles to their programmed feed rates, the probe sensor should be a lot more
// repeatable. If needed, you can disable this behavior by uncommenting the define below.
// #define ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES // Default disabled. Uncomment to enable.
// Enables and configures parking motion methods upon a safety door state. Primarily for OEMs
// that desire this feature for their integrated machines. At the moment, Grbl assumes that
// the parking motion only involves one axis, although the parking implementation was written
// to be easily refactored for any number of motions on different axes by altering the parking
// source code. At this time, Grbl only supports parking one axis (typically the Z-axis) that
// moves in the positive direction upon retracting and negative direction upon restoring position.
// The motion executes with a slow pull-out retraction motion, power-down, and a fast park.
// Restoring to the resume position follows these set motions in reverse: fast restore to
// pull-out position, power-up with a time-out, and plunge back to the original position at the
// slower pull-out rate.
// NOTE: Still a work-in-progress. Machine coordinates must be in all negative space and
// does not work with HOMING_FORCE_SET_ORIGIN enabled. Parking motion also moves only in
// positive direction.
//#define PARKING_ENABLE // Default disabled. Uncomment to enable
// Configure options for the parking motion, if enabled.
#define PARKING_AXIS Z_AXIS // Define which axis that performs the parking motion
#define PARKING_TARGET -5.0 // Parking axis target. In mm, as machine coordinate [-max_travel,0].
#define PARKING_RATE 500.0 // Parking fast rate after pull-out in mm/min.
#define PARKING_PULLOUT_RATE 100.0 // Pull-out/plunge slow feed rate in mm/min.
#define PARKING_PULLOUT_INCREMENT 5.0 // Spindle pull-out and plunge distance in mm. Incremental distance.
// Must be positive value or equal to zero.
// Enables a special set of M-code commands that enables and disables the parking motion.
// These are controlled by `M56`, `M56 P1`, or `M56 Px` to enable and `M56 P0` to disable.
// The command is modal and will be set after a planner sync. Since it is g-code, it is
// executed in sync with g-code commands. It is not a real-time command.
// NOTE: PARKING_ENABLE is required. By default, M56 is active upon initialization. Use
// DEACTIVATE_PARKING_UPON_INIT to set M56 P0 as the power-up default.
// #define ENABLE_PARKING_OVERRIDE_CONTROL // Default disabled. Uncomment to enable
// #define DEACTIVATE_PARKING_UPON_INIT // Default disabled. Uncomment to enable.
// This option will automatically disable the laser during a feed hold by invoking a spindle stop
// override immediately after coming to a stop. However, this also means that the laser still may
// be reenabled by disabling the spindle stop override, if needed. This is purely a safety feature
// to ensure the laser doesn't inadvertently remain powered while at a stop and cause a fire.
#define DISABLE_LASER_DURING_HOLD // Default enabled. Comment to disable.
// Enables a piecewise linear model of the spindle PWM/speed output. Requires a solution by the
// 'fit_nonlinear_spindle.py' script in the /doc/script folder of the repo. See file comments
// on how to gather spindle data and run the script to generate a solution.
// #define ENABLE_PIECEWISE_LINEAR_SPINDLE // Default disabled. Uncomment to enable.
// N_PIECES, RPM_MAX, RPM_MIN, RPM_POINTxx, and RPM_LINE_XX constants are all set and given by
// the 'fit_nonlinear_spindle.py' script solution. Used only when ENABLE_PIECEWISE_LINEAR_SPINDLE
// is enabled. Make sure the constant values are exactly the same as the script solution.
// NOTE: When N_PIECES < 4, unused RPM_LINE and RPM_POINT defines are not required and omitted.
/*
#define N_PIECES 4 // Integer (1-4). Number of piecewise lines used in script solution.
#define RPM_MAX 11686.4 // Max RPM of model. $30 > RPM_MAX will be limited to RPM_MAX.
#define RPM_MIN 202.5 // Min RPM of model. $31 < RPM_MIN will be limited to RPM_MIN.
#define RPM_POINT12 6145.4 // Used N_PIECES >=2. Junction point between lines 1 and 2.
#define RPM_POINT23 9627.8 // Used N_PIECES >=3. Junction point between lines 2 and 3.
#define RPM_POINT34 10813.9 // Used N_PIECES = 4. Junction point between lines 3 and 4.
#define RPM_LINE_A1 3.197101e-03 // Used N_PIECES >=1. A and B constants of line 1.
#define RPM_LINE_B1 -3.526076e-1
#define RPM_LINE_A2 1.722950e-2 // Used N_PIECES >=2. A and B constants of line 2.
#define RPM_LINE_B2 8.588176e+01
#define RPM_LINE_A3 5.901518e-02 // Used N_PIECES >=3. A and B constants of line 3.
#define RPM_LINE_B3 4.881851e+02
#define RPM_LINE_A4 1.203413e-01 // Used N_PIECES = 4. A and B constants of line 4.
#define RPM_LINE_B4 1.151360e+03
*/
#define N_PIECES 3
#define RPM_MAX 23935.2
#define RPM_MIN 2412.2
#define RPM_POINT12 6283.9
#define RPM_POINT23 11866.0
#define RPM_LINE_A1 4.390865e-03
#define RPM_LINE_B1 7.591787e+00
#define RPM_LINE_A2 1.074874e-02
#define RPM_LINE_B2 4.754411e+01
#define RPM_LINE_A3 9.528342e-03
#define RPM_LINE_B3 3.306286e+01
/* ---------------------------------------------------------------------------------------
OEM Single File Configuration Option
Instructions: Paste the cpu_map and default setting definitions below without an enclosing
#ifdef. Comment out the CPU_MAP_xxx and DEFAULT_xxx defines at the top of this file, and
the compiler will ignore the contents of defaults.h and cpu_map.h and use the definitions
below.
*/
// Paste CPU_MAP definitions here.
// Paste default settings definitions here.
#endif

View File

@ -0,0 +1,140 @@
/*
coolant_control.c - coolant control methods
Part of Grbl
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
void coolant_init()
{
#ifdef COOLANT_FLOOD_PIN
pinMode(COOLANT_FLOOD_PIN, OUTPUT);
#endif
#ifdef COOLANT_MIST_PIN
pinMode(COOLANT_MIST_PIN, OUTPUT);
#endif
coolant_stop();
}
// Returns current coolant output state. Overrides may alter it from programmed state.
uint8_t coolant_get_state()
{
uint8_t cl_state = COOLANT_STATE_DISABLE;
#ifdef COOLANT_FLOOD_PIN
#ifdef INVERT_COOLANT_FLOOD_PIN
if (! digitalRead(COOLANT_FLOOD_PIN)) {
#else
if (digitalRead(COOLANT_FLOOD_PIN)) {
#endif
cl_state |= COOLANT_STATE_FLOOD;
}
#endif
#ifdef COOLANT_MIST_PIN
#ifdef INVERT_COOLANT_MIST_PIN
if (! digitalRead(COOLANT_MIST_PIN)) {
#else
if (digitalRead(COOLANT_MIST_PIN)) {
#endif
cl_state |= COOLANT_STATE_MIST;
}
#endif
return(cl_state);
}
// Directly called by coolant_init(), coolant_set_state(), and mc_reset(), which can be at
// an interrupt-level. No report flag set, but only called by routines that don't need it.
void coolant_stop()
{
#ifdef COOLANT_FLOOD_PIN
#ifdef INVERT_COOLANT_FLOOD_PIN
digitalWrite(COOLANT_FLOOD_PIN, 1);
#else
digitalWrite(COOLANT_FLOOD_PIN, 0);
#endif
#endif
#ifdef COOLANT_MIST_PIN
#ifdef INVERT_COOLANT_MIST_PIN
digitalWrite(COOLANT_MIST_PIN, 1);
#else
digitalWrite(COOLANT_MIST_PIN, 0);
#endif
#endif
}
// Main program only. Immediately sets flood coolant running state and also mist coolant,
// if enabled. Also sets a flag to report an update to a coolant state.
// Called by coolant toggle override, parking restore, parking retract, sleep mode, g-code
// parser program end, and g-code parser coolant_sync().
void coolant_set_state(uint8_t mode)
{
if (sys.abort) { return; } // Block during abort.
if (mode == COOLANT_DISABLE) {
coolant_stop();
} else {
#ifdef COOLANT_FLOOD_PIN
if (mode & COOLANT_FLOOD_ENABLE) {
#ifdef INVERT_COOLANT_FLOOD_PIN
digitalWrite(COOLANT_FLOOD_PIN, 0);
#else
digitalWrite(COOLANT_FLOOD_PIN, 1);
#endif
}
#endif
#ifdef COOLANT_MIST_PIN
if (mode & COOLANT_MIST_ENABLE) {
#ifdef INVERT_COOLANT_MIST_PIN
digitalWrite(COOLANT_MIST_PIN, 0);
#else
digitalWrite(COOLANT_MIST_PIN, 1);
#endif
}
#endif
}
sys.report_ovr_counter = 0; // Set to report change immediately
}
// G-code parser entry-point for setting coolant state. Forces a planner buffer sync and bails
// if an abort or check-mode is active.
void coolant_sync(uint8_t mode)
{
if (sys.state == STATE_CHECK_MODE) { return; }
protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
coolant_set_state(mode);
}

View File

@ -0,0 +1,50 @@
/*
coolant_control.h - spindle control methods
Part of Grbl
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 coolant_control_h
#define coolant_control_h
#define COOLANT_NO_SYNC false
#define COOLANT_FORCE_SYNC true
#define COOLANT_STATE_DISABLE 0 // Must be zero
#define COOLANT_STATE_FLOOD bit(0)
#define COOLANT_STATE_MIST bit(1)
// Initializes coolant control pins.
void coolant_init();
// Returns current coolant output state. Overrides may alter it from programmed state.
uint8_t coolant_get_state();
// Immediately disables coolant pins.
void coolant_stop();
// Sets the coolant pins according to state specified.
void coolant_set_state(uint8_t mode);
// G-code parser entry-point for setting coolant states. Checks for and executes additional conditions.
void coolant_sync(uint8_t mode);
#endif

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

View File

@ -0,0 +1,320 @@
/*
defaults.h - defaults settings configuration file
Part of Grbl
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
*/
/* The defaults.h file serves as a central default settings selector for different machine
types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings
files listed here are supplied by users, so your results may vary. However, this should
give you a good starting point as you get to know your machine and tweak the settings for
your nefarious needs.
NOTE: Ensure one and only one of these DEFAULTS_XXX values is defined in config.h */
#ifndef defaults_h
/*
All of these settings check to see if they have been defined already
before defining them. This allows to to easily set them eslewhere.
You only need to set ones that are important or unique to your
machine. The rest will be pulled from here.
*/
// Grbl generic default settings. Should work across different machines.
#ifndef DEFAULT_STEP_PULSE_MICROSECONDS
#define DEFAULT_STEP_PULSE_MICROSECONDS 3 // $0
#endif
#ifndef DEFAULT_STEPPER_IDLE_LOCK_TIME
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 250 // $1 msec (0-254, 255 keeps steppers enabled)
#endif
#ifndef DEFAULT_STEPPING_INVERT_MASK
#define DEFAULT_STEPPING_INVERT_MASK 0 // $2 uint8_t
#endif
#ifndef DEFAULT_DIRECTION_INVERT_MASK
#define DEFAULT_DIRECTION_INVERT_MASK 0 // $3 uint8_
#endif
#ifndef DEFAULT_INVERT_ST_ENABLE
#define DEFAULT_INVERT_ST_ENABLE 0 // $4 boolean
#endif
#ifndef DEFAULT_INVERT_LIMIT_PINS
#define DEFAULT_INVERT_LIMIT_PINS 1 // $5 boolean
#endif
#ifndef DEFAULT_INVERT_PROBE_PIN
#define DEFAULT_INVERT_PROBE_PIN 0 // $6 boolean
#endif
#ifndef DEFAULT_STATUS_REPORT_MASK
#define DEFAULT_STATUS_REPORT_MASK 1 // $10
#endif
#ifndef DEFAULT_JUNCTION_DEVIATION
#define DEFAULT_JUNCTION_DEVIATION 0.01 // $11 mm
#endif
#ifndef DEFAULT_ARC_TOLERANCE
#define DEFAULT_ARC_TOLERANCE 0.002 // $12 mm
#endif
#ifndef DEFAULT_REPORT_INCHES
#define DEFAULT_REPORT_INCHES 0 // $13 false
#endif
#ifndef DEFAULT_SOFT_LIMIT_ENABLE
#define DEFAULT_SOFT_LIMIT_ENABLE 0 // $20 false
#endif
#ifndef DEFAULT_HARD_LIMIT_ENABLE
#define DEFAULT_HARD_LIMIT_ENABLE 0 // $21 false
#endif
#ifndef DEFAULT_HOMING_ENABLE
#define DEFAULT_HOMING_ENABLE 0 // $22 false
#endif
#ifndef DEFAULT_HOMING_DIR_MASK
#define DEFAULT_HOMING_DIR_MASK 3 // $23 move positive dir Z, negative X,Y
#endif
#ifndef DEFAULT_HOMING_FEED_RATE
#define DEFAULT_HOMING_FEED_RATE 200.0 // $24 mm/min
#endif
#ifndef DEFAULT_HOMING_SEEK_RATE
#define DEFAULT_HOMING_SEEK_RATE 2000.0 // $25 mm/min
#endif
#ifndef DEFAULT_HOMING_DEBOUNCE_DELAY
#define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // $26 msec (0-65k)
#endif
#ifndef DEFAULT_HOMING_PULLOFF
#define DEFAULT_HOMING_PULLOFF 1.0 // $27 mm
#endif
// ======== sPINDLE STUFF ====================
#ifndef DEFAULT_SPINDLE_FREQ
#define DEFAULT_SPINDLE_FREQ 5000.0 // $33 Hz (extended set)
#endif
#ifndef DEFAULT_SPINDLE_OFF_VALUE
#define DEFAULT_SPINDLE_OFF_VALUE 0.0 // $34 Percent (extended set)
#endif
#ifndef DEFAULT_SPINDLE_MIN_VALUE
#define DEFAULT_SPINDLE_MIN_VALUE 0.0 // $35 Percent (extended set)
#endif
#ifndef DEFAULT_SPINDLE_MAX_VALUE
#define DEFAULT_SPINDLE_MAX_VALUE 100.0 // $36 Percent (extended set)
#endif
#ifndef DEFAULT_SPINDLE_RPM_MAX
#define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm
#endif
#ifndef DEFAULT_SPINDLE_RPM_MIN
#define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
#endif
#ifndef DEFAULT_LASER_MODE
#define DEFAULT_LASER_MODE 0 // false
#endif
// =========== AXIS RESOLUTION ======
#ifndef DEFAULT_X_STEPS_PER_MM
#define DEFAULT_X_STEPS_PER_MM 800.0
#endif
#ifndef DEFAULT_Y_STEPS_PER_MM
#define DEFAULT_Y_STEPS_PER_MM 800.0
#endif
#ifndef DEFAULT_Z_STEPS_PER_MM
#define DEFAULT_Z_STEPS_PER_MM 800.0
#endif
#ifndef DEFAULT_A_STEPS_PER_MM
#define DEFAULT_A_STEPS_PER_MM 800.0
#endif
#ifndef DEFAULT_B_STEPS_PER_MM
#define DEFAULT_B_STEPS_PER_MM 800.0
#endif
#ifndef DEFAULT_C_STEPS_PER_MM
#define DEFAULT_C_STEPS_PER_MM 800.0
#endif
// ============ AXIS MAX SPPED =========
#ifndef DEFAULT_X_MAX_RATE
#define DEFAULT_X_MAX_RATE 5000.0 // mm/min
#endif
#ifndef DEFAULT_Y_MAX_RATE
#define DEFAULT_Y_MAX_RATE 5000.0 // mm/min
#endif
#ifndef DEFAULT_Z_MAX_RATE
#define DEFAULT_Z_MAX_RATE 5000.0 // mm/min
#endif
#ifndef DEFAULT_A_MAX_RATE
#define DEFAULT_A_MAX_RATE 5000.0 // mm/min
#endif
#ifndef DEFAULT_B_MAX_RATE
#define DEFAULT_B_MAX_RATE 5000.0 // mm/min
#endif
#ifndef DEFAULT_C_MAX_RATE
#define DEFAULT_C_MAX_RATE 5000.0 // mm/min
#endif
// ============== Axis Acceleration =========
#ifndef DEFAULT_X_ACCELERATION
#define DEFAULT_X_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#endif
#ifndef DEFAULT_Y_ACCELERATION
#define DEFAULT_Y_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#endif
#ifndef DEFAULT_Z_ACCELERATION
#define DEFAULT_Z_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#endif
#ifndef DEFAULT_A_ACCELERATION
#define DEFAULT_A_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#endif
#ifndef DEFAULT_B_ACCELERATION
#define DEFAULT_B_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#endif
#ifndef DEFAULT_C_ACCELERATION
#define DEFAULT_C_ACCELERATION (200.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
#endif
// ========= AXIS MAX TRAVEL ============
#ifndef DEFAULT_X_MAX_TRAVEL
#define DEFAULT_X_MAX_TRAVEL 300.0 // $130 mm NOTE: Must be a positive value.
#endif
#ifndef DEFAULT_Y_MAX_TRAVEL
#define DEFAULT_Y_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value.
#endif
#ifndef DEFAULT_Z_MAX_TRAVEL
#define DEFAULT_Z_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value.
#endif
#ifndef DEFAULT_A_TRAVEL
#define DEFAULT_A_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value.
#endif
#ifndef DEFAULT_B_MAX_TRAVEL
#define DEFAULT_B_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value.
#endif
#ifndef DEFAULT_C_MAX_TRAVEL
#define DEFAULT_C_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value.
#endif
// ========== Motor current (SPI Drivers ) =============
#ifndef DEFAULT_X_CURRENT
#define DEFAULT_X_CURRENT 0.25 // $140 current in amps (extended set)
#endif
#ifndef DEFAULT_Y_CURRENT
#define DEFAULT_Y_CURRENT 0.25 // $141 current in amps (extended set)
#endif
#ifndef DEFAULT_Z_CURRENT
#define DEFAULT_Z_CURRENT 0.25 // $142 current in amps (extended set)
#endif
#ifndef DEFAULT_A_CURRENT
#define DEFAULT_A_CURRENT 0.25 // $143 current in amps (extended set)
#endif
#ifndef DEFAULT_B_CURRENT
#define DEFAULT_B_CURRENT 0.25 // $144 current in amps (extended set)
#endif
#ifndef DEFAULT_C_CURRENT
#define DEFAULT_C_CURRENT 0.25 // $145 current in amps (extended set)
#endif
// ========== Motor hold current (SPI Drivers ) =============
#ifndef DEFAULT_X_HOLD_CURRENT
#define DEFAULT_X_HOLD_CURRENT 50 // $150 percent of run current (extended set)
#endif
#ifndef DEFAULT_Y_HOLD_CURRENT
#define DEFAULT_Y_HOLD_CURRENT 50 // $151 percent of run current (extended set)
#endif
#ifndef DEFAULT_Z_HOLD_CURRENT
#define DEFAULT_Z_HOLD_CURRENT 50 // $152 percent of run current (extended set)
#endif
#ifndef DEFAULT_A_HOLD_CURRENT
#define DEFAULT_A_HOLD_CURRENT 50 // $153 percent of run current (extended set)
#endif
#ifndef DEFAULT_B_HOLD_CURRENT
#define DEFAULT_B_HOLD_CURRENT 50 // $154 percent of run current (extended set)
#endif
#ifndef DEFAULT_C_HOLD_CURRENT
#define DEFAULT_C_HOLD_CURRENT 50 // $154 percent of run current (extended set)
#endif
// ========== Microsteps (SPI Drivers ) ================
#ifndef DEFAULT_X_MICROSTEPS
#define DEFAULT_X_MICROSTEPS 16 // $160 micro steps (extended set)
#endif
#ifndef DEFAULT_Y_MICROSTEPS
#define DEFAULT_Y_MICROSTEPS 16 // $161 micro steps (extended set)
#endif
#ifndef DEFAULT_Z_MICROSTEPS
#define DEFAULT_Z_MICROSTEPS 16 // $162 micro steps (extended set)
#endif
#ifndef DEFAULT_A_MICROSTEPS
#define DEFAULT_A_MICROSTEPS 16 // $163 micro steps (extended set)
#endif
#ifndef DEFAULT_B_MICROSTEPS
#define DEFAULT_B_MICROSTEPS 16 // $164 micro steps (extended set)
#endif
#ifndef DEFAULT_C_MICROSTEPS
#define DEFAULT_C_MICROSTEPS 16 // $165 micro steps (extended set)
#endif
// ========== Stallguard (SPI Drivers ) ================
#ifndef DEFAULT_X_STALLGUARD
#define DEFAULT_X_STALLGUARD 16 // $170 stallguard (extended set)
#endif
#ifndef DEFAULT_Y_STALLGUARD
#define DEFAULT_Y_STALLGUARD 16 // $171 stallguard (extended set)
#endif
#ifndef DEFAULT_Z_STALLGUARD
#define DEFAULT_Z_STALLGUARD 16 // $172 stallguard (extended set)
#endif
#ifndef DEFAULT_A_STALLGUARD
#define DEFAULT_A_STALLGUARD 16 // $173 stallguard (extended set)
#endif
#ifndef DEFAULT_B_STALLGUARD
#define DEFAULT_B_STALLGUARD 16 // $174 stallguard (extended set)
#endif
#ifndef DEFAULT_C_STALLGUARD
#define DEFAULT_C_STALLGUARD 16 // $175 stallguard (extended set)
#endif
#endif

View File

@ -0,0 +1,112 @@
/*
espresponse.cpp - GRBL_ESP response class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include "espresponse.h"
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
#include "web_server.h"
#include <WebServer.h>
#endif
#include "report.h"
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
ESPResponseStream::ESPResponseStream(WebServer * webserver){
_header_sent=false;
_webserver = webserver;
_client = CLIENT_WEBUI;
}
#endif
ESPResponseStream::ESPResponseStream(){
_client = CLIENT_INPUT;
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
_header_sent=false;
_webserver = NULL;
#endif
}
ESPResponseStream::ESPResponseStream(uint8_t client, bool byid){
(void)byid; //fake parameter to avoid confusion with pointer one (NULL == 0)
_client = client;
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
_header_sent=false;
_webserver = NULL;
#endif
}
void ESPResponseStream::println(const char *data){
print(data);
if (_client == CLIENT_TELNET) print("\r\n");
else print("\n");
}
//helper to format size to readable string
String ESPResponseStream::formatBytes (uint64_t bytes)
{
if (bytes < 1024) {
return String ((uint16_t)bytes) + " B";
} else if (bytes < (1024 * 1024) ) {
return String ((float)(bytes / 1024.0),2) + " KB";
} else if (bytes < (1024 * 1024 * 1024) ) {
return String ((float)(bytes / 1024.0 / 1024.0),2) + " MB";
} else {
return String ((float)(bytes / 1024.0 / 1024.0 / 1024.0),2) + " GB";
}
}
void ESPResponseStream::print(const char *data){
if (_client == CLIENT_INPUT) return;
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
if (_webserver) {
if (!_header_sent) {
_webserver->setContentLength(CONTENT_LENGTH_UNKNOWN);
_webserver->sendHeader("Content-Type","text/html");
_webserver->sendHeader("Cache-Control","no-cache");
_webserver->send(200);
_header_sent = true;
}
_buffer+=data;
if (_buffer.length() > 1200) {
//send data
_webserver->sendContent(_buffer);
//reset buffer
_buffer = "";
}
return;
}
#endif
if (_client == CLIENT_WEBUI) return; //this is sanity check
grbl_send(_client, data);
}
void ESPResponseStream::flush(){
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
if (_webserver) {
if(_header_sent) {
//send data
if(_buffer.length() > 0)_webserver->sendContent(_buffer);
//close connection
_webserver->sendContent("");
}
_header_sent = false;
_buffer = "";
}
#endif
}

View File

@ -0,0 +1,50 @@
/*
espresponse.h - GRBL_ESP response class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ESPRESPONSE_h
#define ESPRESPONSE_h
#include "config.h"
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
class WebServer;
#endif
class ESPResponseStream{
public:
void print(const char *data);
void println(const char *data);
void flush();
static String formatBytes (uint64_t bytes);
uint8_t client() {return _client;}
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
ESPResponseStream(WebServer * webserver);
#endif
ESPResponseStream(uint8_t client, bool byid = true);
ESPResponseStream();
private:
uint8_t _client;
#if defined (ENABLE_HTTP) && defined(ENABLE_WIFI)
bool _header_sent;
WebServer * _webserver;
String _buffer;
#endif
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,257 @@
/*
gcode.h - rs274/ngc parser.
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 gcode_h
#define gcode_h
// Define modal group internal numbers for checking multiple command violations and tracking the
// type of command that is called in the block. A modal group is a group of g-code commands that are
// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute
// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online,
// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc).
// NOTE: Modal group define values must be sequential and starting from zero.
#define MODAL_GROUP_G0 0 // [G4,G10,G28,G28.1,G30,G30.1,G53,G92,G92.1] Non-modal
#define MODAL_GROUP_G1 1 // [G0,G1,G2,G3,G38.2,G38.3,G38.4,G38.5,G80] Motion
#define MODAL_GROUP_G2 2 // [G17,G18,G19] Plane selection
#define MODAL_GROUP_G3 3 // [G90,G91] Distance mode
#define MODAL_GROUP_G4 4 // [G91.1] Arc IJK distance mode
#define MODAL_GROUP_G5 5 // [G93,G94] Feed rate mode
#define MODAL_GROUP_G6 6 // [G20,G21] Units
#define MODAL_GROUP_G7 7 // [G40] Cutter radius compensation mode. G41/42 NOT SUPPORTED.
#define MODAL_GROUP_G8 8 // [G43.1,G49] Tool length offset
#define MODAL_GROUP_G12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection
#define MODAL_GROUP_G13 10 // [G61] Control mode
#define MODAL_GROUP_M4 11 // [M0,M1,M2,M30] Stopping
#define MODAL_GROUP_M6 14 // [M6] Tool change
#define MODAL_GROUP_M7 12 // [M3,M4,M5] Spindle turning
#define MODAL_GROUP_M8 13 // [M7,M8,M9] Coolant control
#define MODAL_GROUP_M10 14 // [M62, M63] User Defined http://linuxcnc.org/docs/html/gcode/overview.html#_modal_groups
// #define OTHER_INPUT_F 14
// #define OTHER_INPUT_S 15
// #define OTHER_INPUT_T 16
// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used
// internally by the parser to know which command to execute.
// NOTE: Some macro values are assigned specific values to make g-code state reporting and parsing
// compile a litte smaller. Necessary due to being completely out of flash on the 328p. Although not
// ideal, just be careful with values that state 'do not alter' and check both report.c and gcode.c
// to see how they are used, if you need to alter them.
// Modal Group G0: Non-modal actions
#define NON_MODAL_NO_ACTION 0 // (Default: Must be zero)
#define NON_MODAL_DWELL 4 // G4 (Do not alter value)
#define NON_MODAL_SET_COORDINATE_DATA 10 // G10 (Do not alter value)
#define NON_MODAL_GO_HOME_0 28 // G28 (Do not alter value)
#define NON_MODAL_SET_HOME_0 38 // G28.1 (Do not alter value)
#define NON_MODAL_GO_HOME_1 30 // G30 (Do not alter value)
#define NON_MODAL_SET_HOME_1 40 // G30.1 (Do not alter value)
#define NON_MODAL_ABSOLUTE_OVERRIDE 53 // G53 (Do not alter value)
#define NON_MODAL_SET_COORDINATE_OFFSET 92 // G92 (Do not alter value)
#define NON_MODAL_RESET_COORDINATE_OFFSET 102 //G92.1 (Do not alter value)
// Modal Group G1: Motion modes
#define MOTION_MODE_SEEK 0 // G0 (Default: Must be zero)
#define MOTION_MODE_LINEAR 1 // G1 (Do not alter value)
#define MOTION_MODE_CW_ARC 2 // G2 (Do not alter value)
#define MOTION_MODE_CCW_ARC 3 // G3 (Do not alter value)
#define MOTION_MODE_PROBE_TOWARD 140 // G38.2 (Do not alter value)
#define MOTION_MODE_PROBE_TOWARD_NO_ERROR 141 // G38.3 (Do not alter value)
#define MOTION_MODE_PROBE_AWAY 142 // G38.4 (Do not alter value)
#define MOTION_MODE_PROBE_AWAY_NO_ERROR 143 // G38.5 (Do not alter value)
#define MOTION_MODE_NONE 80 // G80 (Do not alter value)
// Modal Group G2: Plane select
#define PLANE_SELECT_XY 0 // G17 (Default: Must be zero)
#define PLANE_SELECT_ZX 1 // G18 (Do not alter value)
#define PLANE_SELECT_YZ 2 // G19 (Do not alter value)
// Modal Group G3: Distance mode
#define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero)
#define DISTANCE_MODE_INCREMENTAL 1 // G91 (Do not alter value)
// Modal Group G4: Arc IJK distance mode
#define DISTANCE_ARC_MODE_INCREMENTAL 0 // G91.1 (Default: Must be zero)
// Modal Group M4: Program flow
#define PROGRAM_FLOW_RUNNING 0 // (Default: Must be zero)
#define PROGRAM_FLOW_PAUSED 3 // M0
#define PROGRAM_FLOW_OPTIONAL_STOP 1 // M1 NOTE: Not supported, but valid and ignored.
#define PROGRAM_FLOW_COMPLETED_M2 2 // M2 (Do not alter value)
#define PROGRAM_FLOW_COMPLETED_M30 30 // M30 (Do not alter value)
// Modal Group G5: Feed rate mode
#define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero)
#define FEED_RATE_MODE_INVERSE_TIME 1 // G93 (Do not alter value)
// Modal Group G6: Units mode
#define UNITS_MODE_MM 0 // G21 (Default: Must be zero)
#define UNITS_MODE_INCHES 1 // G20 (Do not alter value)
// Modal Group G7: Cutter radius compensation mode
#define CUTTER_COMP_DISABLE 0 // G40 (Default: Must be zero)
// Modal Group G13: Control mode
#define CONTROL_MODE_EXACT_PATH 0 // G61 (Default: Must be zero)
// Modal Group M7: Spindle control
#define SPINDLE_DISABLE 0 // M5 (Default: Must be zero)
#define SPINDLE_ENABLE_CW PL_COND_FLAG_SPINDLE_CW // M3 (NOTE: Uses planner condition bit flag)
#define SPINDLE_ENABLE_CCW PL_COND_FLAG_SPINDLE_CCW // M4 (NOTE: Uses planner condition bit flag)
// Modal Group M8: Coolant control
#define COOLANT_DISABLE 0 // M9 (Default: Must be zero)
#define COOLANT_FLOOD_ENABLE PL_COND_FLAG_COOLANT_FLOOD // M8 (NOTE: Uses planner condition bit flag)
#define COOLANT_MIST_ENABLE PL_COND_FLAG_COOLANT_MIST // M7 (NOTE: Uses planner condition bit flag)
// modal Group M10: User I/O control
#define NON_MODAL_IO_ENABLE 1
#define NON_MODAL_IO_DISABLE 2
#define MAX_USER_DIGITAL_PIN 4
// Modal Group G8: Tool length offset
#define TOOL_LENGTH_OFFSET_CANCEL 0 // G49 (Default: Must be zero)
#define TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC 1 // G43.1
#define TOOL_CHANGE 1
// Modal Group G12: Active work coordinate system
// N/A: Stores coordinate system value (54-59) to change to.
// Define parameter word mapping.
#define WORD_F 0
#define WORD_I 1
#define WORD_J 2
#define WORD_K 3
#define WORD_L 4
#define WORD_N 5
#define WORD_P 6
#define WORD_R 7
#define WORD_S 8
#define WORD_T 9
#define WORD_X 10
#define WORD_Y 11
#define WORD_Z 12
#define WORD_A 13
#define WORD_B 14
#define WORD_C 15
// Define g-code parser position updating flags
#define GC_UPDATE_POS_TARGET 0 // Must be zero
#define GC_UPDATE_POS_SYSTEM 1
#define GC_UPDATE_POS_NONE 2
// Define probe cycle exit states and assign proper position updating.
#define GC_PROBE_FOUND GC_UPDATE_POS_SYSTEM
#define GC_PROBE_ABORT GC_UPDATE_POS_NONE
#define GC_PROBE_FAIL_INIT GC_UPDATE_POS_NONE
#define GC_PROBE_FAIL_END GC_UPDATE_POS_TARGET
#ifdef SET_CHECK_MODE_PROBE_TO_START
#define GC_PROBE_CHECK_MODE GC_UPDATE_POS_NONE
#else
#define GC_PROBE_CHECK_MODE GC_UPDATE_POS_TARGET
#endif
// Define gcode parser flags for handling special cases.
#define GC_PARSER_NONE 0 // Must be zero.
#define GC_PARSER_JOG_MOTION bit(0)
#define GC_PARSER_CHECK_MANTISSA bit(1)
#define GC_PARSER_ARC_IS_CLOCKWISE bit(2)
#define GC_PARSER_PROBE_IS_AWAY bit(3)
#define GC_PARSER_PROBE_IS_NO_ERROR bit(4)
#define GC_PARSER_LASER_FORCE_SYNC bit(5)
#define GC_PARSER_LASER_DISABLE bit(6)
#define GC_PARSER_LASER_ISMOTION bit(7)
// NOTE: When this struct is zeroed, the above defines set the defaults for the system.
typedef struct {
uint8_t motion; // {G0,G1,G2,G3,G38.2,G80}
uint8_t feed_rate; // {G93,G94}
uint8_t units; // {G20,G21}
uint8_t distance; // {G90,G91}
// uint8_t distance_arc; // {G91.1} NOTE: Don't track. Only default supported.
uint8_t plane_select; // {G17,G18,G19}
// uint8_t cutter_comp; // {G40} NOTE: Don't track. Only default supported.
uint8_t tool_length; // {G43.1,G49}
uint8_t coord_select; // {G54,G55,G56,G57,G58,G59}
// uint8_t control; // {G61} NOTE: Don't track. Only default supported.
uint8_t program_flow; // {M0,M1,M2,M30}
uint8_t coolant; // {M7,M8,M9}
uint8_t spindle; // {M3,M4,M5}
uint8_t tool_change; // {M6}
uint8_t io_control; // {M62, M63}
} gc_modal_t;
typedef struct {
float f; // Feed
float ijk[N_AXIS]; // I,J,K Axis arc offsets
uint8_t l; // G10 or canned cycles parameters
int32_t n; // Line number
float p; // G10 or dwell parameters
// float q; // G82 peck drilling
float r; // Arc radius
float s; // Spindle speed
uint8_t t; // Tool selection
float xyz[N_AXIS]; // X,Y,Z Translational axes
} gc_values_t;
typedef struct {
gc_modal_t modal;
float spindle_speed; // RPM
float feed_rate; // Millimeters/min
uint8_t tool; // Tracks tool number. NOT USED.
int32_t line_number; // Last line number sent
float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code
float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine
// position in mm. Loaded from EEPROM when called.
float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to
// machine zero in mm. Non-persistent. Cleared upon reset and boot.
float tool_length_offset; // Tracks tool length offset value when enabled.
} parser_state_t;
extern parser_state_t gc_state;
typedef struct {
uint8_t non_modal_command;
gc_modal_t modal;
gc_values_t values;
} parser_block_t;
// Initialize the parser
void gc_init();
// Execute one block of rs275/ngc/g-code
uint8_t gc_execute_line(char *line, uint8_t client);
// Set g-code parser position. Input in steps.
void gc_sync_position();
#endif

View File

@ -0,0 +1,95 @@
/*
grbl.h - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
*/
// Grbl versioning system
#define GRBL_VERSION "1.1f"
#define GRBL_VERSION_BUILD "20191208"
//#include <sdkconfig.h>
#include <Arduino.h>
#include <EEPROM.h>
#include <driver/rmt.h>
#include <esp_task_wdt.h>
#include <freertos/task.h>
#include "driver/timer.h"
// Define the Grbl system include files. NOTE: Do not alter organization.
#include "config.h"
#include "nuts_bolts.h"
#include "cpu_map.h"
#include "tdef.h"
#include "defaults.h"
#include "settings.h"
#include "system.h"
#include "planner.h"
#include "coolant_control.h"
#include "grbl_eeprom.h"
#include "gcode.h"
#include "grbl_limits.h"
#include "motion_control.h"
#include "print.h"
#include "probe.h"
#include "protocol.h"
#include "report.h"
#include "serial.h"
#include "spindle_control.h"
#include "stepper.h"
#include "jog.h"
#include "inputbuffer.h"
#ifdef ENABLE_BLUETOOTH
#include "BTconfig.h"
#endif
#ifdef ENABLE_SD_CARD
#include "grbl_sd.h"
#endif
#ifdef ENABLE_WIFI
#include "wificonfig.h"
#ifdef ENABLE_HTTP
#include "serial2socket.h"
#endif
#ifdef ENABLE_TELNET
#include "telnet_server.h"
#endif
#ifdef ENABLE_NOTIFICATIONS
#include "notifications_service.h"
#endif
#endif
#include "servo_pen.h"
#include "solenoid_pen.h"
#ifdef USE_SERVO_AXES
#include "servo_axis.h"
#endif
#ifdef USE_TRINAMIC
#include "grbl_trinamic.h"
#endif
#ifdef USE_UNIPOLAR
#include "grbl_unipolar.h"
#endif

View File

@ -0,0 +1,43 @@
/*
eeprom.cpp - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size) {
unsigned char checksum = 0;
for(; size > 0; size--) {
checksum = (checksum << 1) || (checksum >> 7);
checksum += *source;
EEPROM.write(destination++, *(source++));
}
EEPROM.write(destination, checksum);
EEPROM.commit();
}
int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) {
unsigned char data, checksum = 0;
for(; size > 0; size--) {
data = EEPROM.read(source++);
checksum = (checksum << 1) || (checksum >> 7);
checksum += data;
*(destination++) = data;
}
return(checksum == EEPROM.read(source));
}

View File

@ -0,0 +1,31 @@
/*
eeprom.h - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 eeprom_memcpy_h
#define eeprom_memcpy_h
#include "grbl.h"
//unsigned char eeprom_get_char(unsigned int addr);
//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);
int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size);
#endif

View File

@ -0,0 +1,496 @@
/*
limits.c - code pertaining to limit-switches and performing the homing cycle
Part of Grbl
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
2018-12-29 - Wolfgang Lienbacher renamed file from limits.h to grbl_limits.h
fixing ambiguation issues with limit.h in the esp32 Arduino Framework
when compiling with VS-Code/PlatformIO.
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 "grbl.h"
xQueueHandle limit_sw_queue; // used by limit switch debouncing
// Homing axis search distance multiplier. Computed by this value times the cycle travel.
#ifndef HOMING_AXIS_SEARCH_SCALAR
#define HOMING_AXIS_SEARCH_SCALAR 1.1 // Must be > 1 to ensure limit switch will be engaged.
#endif
#ifndef HOMING_AXIS_LOCATE_SCALAR
#define HOMING_AXIS_LOCATE_SCALAR 5.0 // Must be > 1 to ensure limit switch is cleared.
#endif
void IRAM_ATTR isr_limit_switches()
{
// Ignore limit switches if already in an alarm state or in-process of executing an alarm.
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending
// moves in the planner and serial buffers are all cleared and newly sent blocks will be
// locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
// limit setting if their limits are constantly triggering after a reset and move their axes.
if ( ( sys.state != STATE_ALARM) & (bit_isfalse(sys.state, STATE_HOMING)) ) {
if (!(sys_rt_exec_alarm)) {
#ifdef ENABLE_SOFTWARE_DEBOUNCE
// we will start a task that will recheck the switches after a small delay
int evt;
xQueueSendFromISR(limit_sw_queue, &evt, NULL);
#else
#ifdef HARD_LIMIT_FORCE_STATE_CHECK
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
#else
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
#endif
#endif
}
}
}
// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after
// completing. Homing is a special motion case, which involves rapid uncontrolled stops to locate
// the trigger point of the limit switches. The rapid stops are handled by a system level axis lock
// mask, which prevents the stepper algorithm from executing step pulses. Homing motions typically
// circumvent the processes for executing motions in normal operation.
// NOTE: Only the abort realtime command can interrupt this process.
// TODO: Move limit pin-specific calls to a general function for portability.
void limits_go_home(uint8_t cycle_mask)
{
if (sys.abort) { return; } // Block if system reset has been issued.
// Initialize plan data struct for homing motion. Spindle and coolant are disabled.
plan_line_data_t plan_data;
plan_line_data_t *pl_data = &plan_data;
memset(pl_data,0,sizeof(plan_line_data_t));
pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE);
#ifdef USE_LINE_NUMBERS
pl_data->line_number = HOMING_CYCLE_LINE_NUMBER;
#endif
// Initialize variables used for homing computations.
uint8_t n_cycle = (2*N_HOMING_LOCATE_CYCLE+1);
uint8_t step_pin[N_AXIS];
float target[N_AXIS];
float max_travel = 0.0;
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
// Initialize step pin masks
step_pin[idx] = get_step_pin_mask(idx);
#ifdef COREXY
if ((idx==A_MOTOR)||(idx==B_MOTOR)) { step_pin[idx] = (get_step_pin_mask(X_AXIS)|get_step_pin_mask(Y_AXIS)); }
#endif
if (bit_istrue(cycle_mask,bit(idx))) {
// 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)*settings.max_travel[idx]);
}
}
// Set search mode with approach at seek rate to quickly engage the specified cycle_mask limit switches.
bool approach = true;
float homing_rate = settings.homing_seek_rate;
uint8_t limit_state, axislock, n_active_axis;
do {
system_convert_array_steps_to_mpos(target,sys_position);
// Initialize and declare variables needed for homing routine.
axislock = 0;
n_active_axis = 0;
for (idx=0; idx<N_AXIS; idx++) {
// Set target location for active axes and setup computation for homing rate.
if (bit_istrue(cycle_mask,bit(idx))) {
n_active_axis++;
#ifdef COREXY
if (idx == X_AXIS) {
int32_t axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
sys_position[A_MOTOR] = axis_position;
sys_position[B_MOTOR] = -axis_position;
} else if (idx == Y_AXIS) {
int32_t axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
sys_position[A_MOTOR] = sys_position[B_MOTOR] = axis_position;
} else {
sys_position[Z_AXIS] = 0;
}
#else
sys_position[idx] = 0;
#endif
// Set target direction based on cycle mask and homing cycle approach state.
// NOTE: This happens to compile smaller than any other implementation tried.
if (bit_istrue(settings.homing_dir_mask,bit(idx))) {
if (approach) { target[idx] = -max_travel; }
else { target[idx] = max_travel; }
} else {
if (approach) { target[idx] = max_travel; }
else { target[idx] = -max_travel; }
}
// Apply axislock to the step port pins active in this cycle.
axislock |= step_pin[idx];
}
}
homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
sys.homing_axis_lock = axislock;
// Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
pl_data->feed_rate = homing_rate; // Set current homing rate.
plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion.
sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags.
st_prep_buffer(); // Prep and fill segment buffer from newly planned block.
st_wake_up(); // Initiate motion
do {
if (approach) {
// Check limit state. Lock out cycle axes when they change.
limit_state = limits_get_state();
for (idx=0; idx<N_AXIS; idx++) {
if (axislock & step_pin[idx]) {
if (limit_state & (1 << idx)) {
#ifdef COREXY
if (idx==Z_AXIS) { axislock &= ~(step_pin[Z_AXIS]); }
else { axislock &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); }
#else
axislock &= ~(step_pin[idx]);
#endif
}
}
}
sys.homing_axis_lock = axislock;
}
st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.
// Exit routines: No time to run protocol_execute_realtime() in this loop.
if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP)) {
uint8_t rt_exec = sys_rt_exec_state;
// Homing failure condition: Reset issued during cycle.
if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
// Homing failure condition: Safety door was opened.
if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); }
// Homing failure condition: Limit switch still engaged after pull-off motion
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.
if (approach && (rt_exec & EXEC_CYCLE_STOP)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH); }
if (sys_rt_exec_alarm) {
mc_reset(); // Stop motors, if they are running.
protocol_execute_realtime();
return;
} else {
// Pull-off motion complete. Disable CYCLE_STOP from executing.
system_clear_exec_state_flag(EXEC_CYCLE_STOP);
break;
}
}
} while (STEP_MASK & axislock);
st_reset(); // Immediately force kill steppers and reset step segment buffer.
delay_ms(settings.homing_debounce_delay); // Delay to allow transient dynamics to dissipate.
// Reverse direction and reset homing rate for locate cycle(s).
approach = !approach;
// After first cycle, homing enters locating phase. Shorten search to pull-off distance.
if (approach) {
max_travel = settings.homing_pulloff*HOMING_AXIS_LOCATE_SCALAR;
homing_rate = settings.homing_feed_rate;
} else {
max_travel = settings.homing_pulloff;
homing_rate = settings.homing_seek_rate;
}
} while (n_cycle-- > 0);
// The active cycle axes should now be homed and machine limits have been located. By
// default, Grbl defines machine space as all negative, as do most CNCs. Since limit switches
// can be on either side of an axes, check and set axes machine zero appropriately. Also,
// set up pull-off maneuver from axes limit switches that have been homed. This provides
// some initial clearance off the switches and should also help prevent them from falsely
// triggering when hard limits are enabled or when more than one axes shares a limit pin.
int32_t set_axis_position;
// Set machine positions for homed limit switches. Don't update non-homed axes.
for (idx=0; idx<N_AXIS; idx++) {
// NOTE: settings.max_travel[] is stored as a negative value.
if (cycle_mask & bit(idx)) {
#ifdef HOMING_FORCE_SET_ORIGIN
set_axis_position = 0;
#else
if ( bit_istrue(settings.homing_dir_mask,bit(idx)) ) {
#ifdef HOMING_FORCE_POSITIVE_SPACE
set_axis_position = 0; //lround(settings.homing_pulloff*settings.steps_per_mm[idx]);
#else
set_axis_position = lround((settings.max_travel[idx]+settings.homing_pulloff)*settings.steps_per_mm[idx]);
#endif
} else {
#ifdef HOMING_FORCE_POSITIVE_SPACE
set_axis_position = lround((-settings.max_travel[idx]-settings.homing_pulloff)*settings.steps_per_mm[idx]);
#else
set_axis_position = lround(-settings.homing_pulloff*settings.steps_per_mm[idx]);
#endif
}
#endif
#ifdef COREXY
if (idx==X_AXIS) {
int32_t off_axis_position = system_convert_corexy_to_y_axis_steps(sys_position);
sys_position[A_MOTOR] = set_axis_position + off_axis_position;
sys_position[B_MOTOR] = set_axis_position - off_axis_position;
} else if (idx==Y_AXIS) {
int32_t off_axis_position = system_convert_corexy_to_x_axis_steps(sys_position);
sys_position[A_MOTOR] = off_axis_position + set_axis_position;
sys_position[B_MOTOR] = off_axis_position - set_axis_position;
} else {
sys_position[idx] = set_axis_position;
}
#else
sys_position[idx] = set_axis_position;
#endif
}
}
sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation.
}
void limits_init()
{
#ifndef DISABLE_LIMIT_PIN_PULL_UP
#ifdef X_LIMIT_PIN
pinMode(X_LIMIT_PIN, INPUT_PULLUP); // input with pullup
#endif
#ifdef Y_LIMIT_PIN
pinMode(Y_LIMIT_PIN, INPUT_PULLUP);
#endif
#ifdef Z_LIMIT_PIN
pinMode(Z_LIMIT_PIN, INPUT_PULLUP);
#endif
#ifdef A_LIMIT_PIN
pinMode(A_LIMIT_PIN, INPUT_PULLUP);
#endif
#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
limit_sw_queue = xQueueCreate(10, sizeof( int ));
xTaskCreate(limitCheckTask,
"limitCheckTask",
2048,
NULL,
5, // priority
NULL);
}
// Disables hard limits.
void limits_disable()
{
detachInterrupt(X_LIMIT_BIT);
detachInterrupt(Y_LIMIT_BIT);
detachInterrupt(Z_LIMIT_BIT);
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
// 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.
uint8_t limits_get_state()
{
uint8_t limit_state = 0;
uint8_t pin = 0;
#ifdef X_LIMIT_PIN
pin += digitalRead(X_LIMIT_PIN);
#endif
#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
//grbl_sendf(CLIENT_SERIAL, "[MSG: Limit pins %d]\r\n", pin);
#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;
}
//grbl_sendf(CLIENT_SERIAL, "[MSG: Limit Inverted %d]\r\n", pin);
if (pin) {
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
if (pin & get_limit_pin_mask(idx)) {
limit_state |= (1 << idx);
}
}
}
//grbl_sendf(CLIENT_SERIAL, "[MSG: Limit State %d]\r\n", limit_state);
return(limit_state);
}
// Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed,
// the workspace volume is in all negative space, and the system is in normal operation.
// NOTE: Used by jogging to limit travel within soft-limit volume.
void limits_soft_check(float *target)
{
if (system_check_travel_limits(target)) {
sys.soft_limit = true;
// Force feed hold if cycle is active. All buffered blocks are guaranteed to be within
// workspace volume so just come to a controlled stop so position is not lost. When complete
// enter alarm mode.
if (sys.state == STATE_CYCLE) {
system_set_exec_state_flag(EXEC_FEED_HOLD);
do {
protocol_execute_realtime();
if (sys.abort) { return; }
} while ( sys.state != STATE_IDLE );
}
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
system_set_exec_alarm(EXEC_ALARM_SOFT_LIMIT); // Indicate soft limit critical event
protocol_execute_realtime(); // Execute to enter critical event loop and system abort
return;
}
}
// this is the task
void limitCheckTask(void *pvParameters)
{
while(true) {
int evt;
xQueueReceive(limit_sw_queue, &evt, portMAX_DELAY); // block until receive queue
vTaskDelay( DEBOUNCE_PERIOD / portTICK_PERIOD_MS ); // delay a while
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
}
}
// return true if the axis is defined as a squared axis
// Squaring: is used on gantry type axes that have two motors
// Each motor with touch off its own switch to square the axis
bool axis_is_squared(uint8_t axis_mask)
{
// Note: multi-axis homing returns false because it will not match any of the following
if (axis_mask == (1<<X_AXIS)) {
#ifdef X_AXIS_SQUARING
return true;
#endif
}
if (axis_mask == (1<<Y_AXIS)) {
#ifdef Y_AXIS_SQUARING
return true;
#endif
}
if (axis_mask == (1<<Z_AXIS)) {
#ifdef Z_AXIS_SQUARING
return true;
#endif
}
return false;
}

View File

@ -0,0 +1,57 @@
/*
limits.h - code pertaining to limit-switches and performing the homing cycle
Part of Grbl
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
2018-12-29 - Wolfgang Lienbacher renamed file from limits.h to grbl_limits.h
fixing ambiguation issues with limit.h in the esp32 Arduino Framework
when compiling with VS-Code/PlatformIO.
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 grbl_limits_h
#define grbl_limits_h
#define SQUARING_MODE_DUAL 0 // both motors run
#define SQUARING_MODE_A 1 // A motor runs
#define SQUARING_MODE_B 2 // B motor runs
// Initialize the limits module
void limits_init();
// Disables hard limits.
void limits_disable();
// Returns limit state as a bit-wise uint8 variable.
uint8_t limits_get_state();
// Perform one portion of the homing cycle based on the input settings.
void limits_go_home(uint8_t cycle_mask);
// Check for soft limit violations
void limits_soft_check(float *target);
void isr_limit_switches();
bool axis_is_squared(uint8_t axis_mask);
// A task that runs after a limit switch interrupt.
void limitCheckTask(void *pvParameters);
#endif

View File

@ -0,0 +1,233 @@
/*
grbl_sd.cpp - Adds SD Card Features to Grbl_ESP32
Part of Grbl_ESP32
Copyright (c) 2018 Barton Dring Buildlog.net
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 "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;
bool SD_ready_next = false; // Grbl has processed a line and is waiting for another
uint8_t SD_client = CLIENT_SERIAL;
uint32_t sd_current_line_number; // stores the most recent line number read from the SD
static char comment[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
// attempt to mount the SD card
/*bool sd_mount()
{
if(!SD.begin()) {
report_status_message(STATUS_SD_FAILED_MOUNT, CLIENT_SERIAL);
return false;
}
return true;
}*/
void listDir(fs::FS &fs, const char * dirname, uint8_t levels, uint8_t client)
{
//char temp_filename[128]; // to help filter by extension TODO: 128 needs a definition based on something
File root = fs.open(dirname);
if(!root) {
report_status_message(STATUS_SD_FAILED_OPEN_DIR, client);
return;
}
if(!root.isDirectory()) {
report_status_message(STATUS_SD_DIR_NOT_FOUND, client);
return;
}
File file = root.openNextFile();
while(file) {
if(file.isDirectory()) {
if(levels) {
listDir(fs, file.name(), levels -1, client);
}
} else {
grbl_sendf(CLIENT_ALL, "[FILE:%s|SIZE:%d]\r\n", file.name(), file.size());
}
file = root.openNextFile();
}
}
boolean openFile(fs::FS &fs, const char * path)
{
myFile = fs.open(path);
if(!myFile) {
//report_status_message(STATUS_SD_FAILED_READ, CLIENT_SERIAL);
return false;
}
set_sd_state(SDCARD_BUSY_PRINTING);
SD_ready_next = false; // this will get set to true when Grbl issues "ok" message
sd_current_line_number = 0;
return true;
}
boolean closeFile()
{
if(!myFile) {
return false;
}
set_sd_state(SDCARD_IDLE);
SD_ready_next = false;
sd_current_line_number = 0;
myFile.close();
return true;
}
/*
read a line from the SD card
strip whitespace
strip comments per http://linuxcnc.org/docs/ja/html/gcode/overview.html#gcode:comments
make uppercase
return true if a line is
*/
boolean readFileLine(char *line)
{
char c;
uint8_t index = 0;
uint8_t line_flags = 0;
uint8_t comment_char_counter = 0;
if (!myFile) {
report_status_message(STATUS_SD_FAILED_READ, SD_client);
return false;
}
sd_current_line_number += 1;
while(myFile.available()) {
c = myFile.read();
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;
}
}
// some files end without a newline
if (index !=0) {
line[index] = '\0';
return true;
} else { // empty line after new line
return false;
}
}
// return a percentage complete 50.5 = 50.5%
float sd_report_perc_complete()
{
if (!myFile) {
return 0.0;
}
return ((float)myFile.position() / (float)myFile.size() * 100.0);
}
uint32_t sd_get_current_line_number()
{
return sd_current_line_number;
}
uint8_t sd_state = SDCARD_IDLE;
uint8_t get_sd_state(bool refresh)
{
#if defined(SDCARD_DET_PIN) && SDCARD_SD_PIN != -1
//no need to go further if SD detect is not correct
if (!((digitalRead (SDCARD_DET_PIN) == SDCARD_DET_VAL) ? true : false)) {
sd_state = SDCARD_NOT_PRESENT;
return sd_state;
}
#endif
//if busy doing something return state
if (!((sd_state == SDCARD_NOT_PRESENT) || (sd_state == SDCARD_IDLE))) {
return sd_state;
}
if (!refresh) {
return sd_state; //to avoid refresh=true + busy to reset SD and waste time
}
//SD is idle or not detected, let see if still the case
SD.end();
sd_state = SDCARD_NOT_PRESENT;
//using default value for speed ? should be parameter
//refresh content if card was removed
if (SD.begin((GRBL_SPI_SS == -1)?SS:GRBL_SPI_SS, SPI, GRBL_SPI_FREQ)) {
if ( SD.cardSize() > 0 )sd_state = SDCARD_IDLE;
}
return sd_state;
}
uint8_t set_sd_state(uint8_t flag)
{
sd_state = flag;
return sd_state;
}
void sd_get_current_filename(char* name)
{
if (myFile) {
strcpy(name, myFile.name());
} else {
name[0] = 0;
}
}

View File

@ -0,0 +1,53 @@
/*
* Connect the SD card to the following pins:
*
* SD Card | ESP32
* D2 -
* D3 SS
* CMD MOSI
* VSS GND
* VDD 3.3V
* CLK SCK
* VSS GND
* D0 MISO
* D1 -
*/
#ifndef grbl_sd_h
#define grbl_sd_h
#include "grbl.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#define FILE_TYPE_COUNT 5 // number of acceptable gcode file types in array
#define SDCARD_DET_PIN -1
#define SDCARD_DET_VAL 0
#define SDCARD_IDLE 0
#define SDCARD_NOT_PRESENT 1
#define SDCARD_BUSY_PRINTING 2
#define SDCARD_BUSY_UPLOADING 4
#define SDCARD_BUSY_PARSING 8
extern bool SD_ready_next; // Grbl has processed a line and is waiting for another
extern uint8_t SD_client;
//bool sd_mount();
uint8_t get_sd_state(bool refresh);
uint8_t set_sd_state(uint8_t flag);
void listDir(fs::FS &fs, const char * dirname, uint8_t levels, uint8_t client);
boolean openFile(fs::FS &fs, const char * path);
boolean closeFile();
boolean readFileLine(char *line);
void readFile(fs::FS &fs, const char * path);
float sd_report_perc_complete();
uint32_t sd_get_current_line_number();
void sd_get_current_filename(char* name);
#endif

View File

@ -0,0 +1,231 @@
/*
grbl_trinamic.cpp - Support for Trinamic Stepper Drivers SPI Mode
using the TMCStepper library
Part of Grbl_ESP32
Copyright (c) 2019 Barton Dring for Buildlog.net LLC
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/>.
*/
#include "grbl.h"
#ifdef USE_TRINAMIC
/*
The drivers can use SPI daisy chaining to allow the use of only (1) CS_PIN.
The PCB must be designed for this, with SDO pins being coonect to the
next driver's SDI pin. The final SDO goes back to the controller.
This is setup in cpu_map.
add #define TRINAMIC_DAISY_CHAIN to your map
Make every axis CS_PIN definition be for the same pin like this...
#define X_CS_PIN GPIO_NUM_17
#define Y_CS_PIN GPIO_NUM_17
...etc.
Indexes are assigned to each axis in daisy chain mode as shown below.
This assumes your first SPI driver axis is X and there are no gaps until
the last SPI driver.
*/
#ifndef TRINAMIC_DAISY_CHAIN
#define X_DRIVER_SPI_INDEX -1
#define Y_DRIVER_SPI_INDEX -1
#define Z_DRIVER_SPI_INDEX -1
#define A_DRIVER_SPI_INDEX -1
#define B_DRIVER_SPI_INDEX -1
#define C_DRIVER_SPI_INDEX -1
#else
#define X_DRIVER_SPI_INDEX 1
#define Y_DRIVER_SPI_INDEX 2
#define Z_DRIVER_SPI_INDEX 3
#define A_DRIVER_SPI_INDEX 4
#define B_DRIVER_SPI_INDEX 5
#define C_DRIVER_SPI_INDEX 6
#endif
// TODO try to use the #define ## method to clean this up
//#define DRIVER(driver, axis) driver##Stepper = TRINAMIC_axis## = driver##Stepper(axis##_CS_PIN, axis##_RSENSE);
#ifdef X_TRINAMIC
#ifdef X_DRIVER_TMC2130
TMC2130Stepper TRINAMIC_X = TMC2130Stepper(X_CS_PIN, X_RSENSE, X_DRIVER_SPI_INDEX);
#endif
#ifdef X_DRIVER_TMC2209
TMC2209Stepper TRINAMIC_X = TMC2209Stepper(X_CS_PIN, X_RSENSE, X_DRIVER_SPI_INDEX);
#endif
#ifdef X_DRIVER_TMC5160
TMC5160Stepper TRINAMIC_X = TMC5160Stepper(X_CS_PIN, X_RSENSE, X_DRIVER_SPI_INDEX);
#endif
#endif
#ifdef Y_TRINAMIC
#ifdef Y_DRIVER_TMC2130
TMC2130Stepper TRINAMIC_Y = TMC2130Stepper(Y_CS_PIN, Y_RSENSE, Y_DRIVER_SPI_INDEX);
#endif
#ifdef Y_DRIVER_TMC2209
TMC2209Stepper TRINAMIC_Y = TMC2209Stepper(Y_CS_PIN, Y_RSENSE, Y_DRIVER_SPI_INDEX);
#endif
#ifdef Y_DRIVER_TMC5160
TMC5160Stepper TRINAMIC_Y = TMC5160Stepper(Y_CS_PIN, Y_RSENSE, Y_DRIVER_SPI_INDEX);
#endif
#endif
#ifdef Z_TRINAMIC
#ifdef Z_DRIVER_TMC2130
TMC2130Stepper TRINAMIC_Z = TMC2130Stepper(Z_CS_PIN, Z_RSENSE, Z_DRIVER_SPI_INDEX);
#endif
#ifdef Z_DRIVER_TMC2209
TMC2209Stepper TRINAMIC_Z = TMC2209Stepper(Z_CS_PIN, Z_RSENSE, Z_DRIVER_SPI_INDEX);
#endif
#ifdef Z_DRIVER_TMC5160
TMC5160Stepper TRINAMIC_Z = TMC5160Stepper(Z_CS_PIN, Z_RSENSE, Z_DRIVER_SPI_INDEX);
#endif
#endif
#ifdef A_TRINAMIC
#ifdef A_DRIVER_TMC2130
TMC2130Stepper TRINAMIC_A = TMC2130Stepper(A_CS_PIN, A_RSENSE, A_DRIVER_SPI_INDEX);
#endif
#ifdef A_DRIVER_TMC2209
TMC2209Stepper TRINAMIC_A = TMC2209Stepper(A_CS_PIN, A_RSENSE, A_DRIVER_SPI_INDEX);
#endif
#ifdef A_DRIVER_TMC5160
TMC5160Stepper TRINAMIC_A = TMC5160Stepper(A_CS_PIN, A_RSENSE, A_DRIVER_SPI_INDEX);
#endif
#endif
#ifdef B_TRINAMIC
#ifdef B_DRIVER_TMC2130
TMC2130Stepper TRINAMIC_B = TMC2130Stepper(B_CS_PIN, B_RSENSE, B_DRIVER_SPI_INDEX);
#endif
#ifdef B_DRIVER_TMC2209
TMC2209Stepper TRINAMIC_B = TMC2209Stepper(B_CS_PIN, B_RSENSE, B_DRIVER_SPI_INDEX);
#endif
#ifdef B_DRIVER_TMC5160
TMC5160Stepper TRINAMIC_B = TMC5160Stepper(B_CS_PIN, B_RSENSE, B_DRIVER_SPI_INDEX);
#endif
#endif
#ifdef C_TRINAMIC
#ifdef C_DRIVER_TMC2130
TMC2130Stepper TRINAMIC_c = TMC2130Stepper(C_CS_PIN, C_RSENSE, C_DRIVER_SPI_INDEX);
#endif
#ifdef C_DRIVER_TMC2209
TMC2209Stepper TRINAMIC_C = TMC2209Stepper(C_CS_PIN, C_RSENSE, C_DRIVER_SPI_INDEX);
#endif
#ifdef C_DRIVER_TMC5160
TMC5160Stepper TRINAMIC_C = TMC5160Stepper(C_CS_PIN, C_RSENSE, C_DRIVER_SPI_INDEX);
#endif
#endif
// TODO ABC Axes
void Trinamic_Init()
{
grbl_sendf(CLIENT_SERIAL, "[MSG:TMCStepper Init using Library Ver 0x%06x]\r\n", TMCSTEPPER_VERSION);
SPI.begin();
#ifdef X_TRINAMIC
TRINAMIC_X.begin(); // Initiate pins and registries
TRINAMIC_X.toff(5);
TRINAMIC_X.microsteps(settings.microsteps[X_AXIS]);
TRINAMIC_X.rms_current(settings.current[X_AXIS] * 1000.0, settings.hold_current[X_AXIS]/100.0);
TRINAMIC_X.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_X.pwm_autoscale(1);
#endif
#ifdef Y_TRINAMIC
TRINAMIC_Y.begin(); // Initiate pins and registries
TRINAMIC_Y.toff(5);
TRINAMIC_Y.microsteps(settings.microsteps[Y_AXIS]);
TRINAMIC_X.rms_current(settings.current[Y_AXIS] * 1000.0, settings.hold_current[Y_AXIS]/100.0);
TRINAMIC_Y.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_Y.pwm_autoscale(1);
#endif
#ifdef Z_TRINAMIC
TRINAMIC_Z.begin(); // Initiate pins and registries
TRINAMIC_Z.toff(5);
TRINAMIC_Z.microsteps(settings.microsteps[Z_AXIS]);
TRINAMIC_X.rms_current(settings.current[Z_AXIS] * 1000.0, settings.hold_current[Z_AXIS]/100.0);
TRINAMIC_Z.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_Z.pwm_autoscale(1);
#endif
#ifdef A_TRINAMIC
TRINAMIC_A.begin(); // Initiate pins and registries
TRINAMIC_A.toff(5);
TRINAMIC_A.microsteps(settings.microsteps[A_AXIS]);
TRINAMIC_X.rms_current(settings.current[A_AXIS] * 1000.0, settings.hold_current[A_AXIS]/100.0);
TRINAMIC_A.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_A.pwm_autoscale(1);
#endif
#ifdef B_TRINAMIC
TRINAMIC_B.begin(); // Initiate pins and registries
TRINAMIC_B.toff(5);
TRINAMIC_B.microsteps(settings.microsteps[B_AXIS]);
TTRINAMIC_X.rms_current(settings.current[B_AXIS] * 1000.0, settings.hold_current[B_AXIS]/100.0);
TRINAMIC_B.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_B.pwm_autoscale(1);
#endif
#ifdef C_TRINAMIC
TRINAMIC_C.begin(); // Initiate pins and registries
TRINAMIC_C.toff(5);
TRINAMIC_C.microsteps(settings.microsteps[C_AXIS]);
TRINAMIC_X.rms_current(settings.current[C_AXIS] * 1000.0, settings.hold_current[C_AXIS]/100.0);
TRINAMIC_C.en_pwm_mode(1); // Enable extremely quiet stepping
TRINAMIC_C.pwm_autoscale(1);
#endif
}
void trinamic_change_settings()
{
#ifdef X_TRINAMIC
TRINAMIC_X.microsteps(settings.microsteps[X_AXIS]);
TRINAMIC_X.rms_current(settings.current[X_AXIS] * 1000.0, settings.hold_current[X_AXIS]/100.0);
#endif
#ifdef Y_TRINAMIC
TRINAMIC_Y.microsteps(settings.microsteps[Y_AXIS]);
TRINAMIC_X.rms_current(settings.current[Y_AXIS] * 1000.0, settings.hold_current[Y_AXIS]/100.0);
#endif
#ifdef Z_TRINAMIC
TRINAMIC_Z.microsteps(settings.microsteps[Z_AXIS]);
TRINAMIC_X.rms_current(settings.current[Z_AXIS] * 1000.0, settings.hold_current[Z_AXIS]/100.0);
#endif
#ifdef A_TRINAMIC
TRINAMIC_A.microsteps(settings.microsteps[A_AXIS]);
TRINAMIC_X.rms_current(settings.current[A_AXIS] * 1000.0, settings.hold_current[A_AXIS]/100.0);
#endif
#ifdef B_TRINAMIC
TRINAMIC_B.microsteps(settings.microsteps[B_AXIS]);
TTRINAMIC_X.rms_current(settings.current[B_AXIS] * 1000.0, settings.hold_current[B_AXIS]/100.0);
#endif
#ifdef C_TRINAMIC
TRINAMIC_C.microsteps(settings.microsteps[C_AXIS]);
TRINAMIC_X.rms_current(settings.current[C_AXIS] * 1000.0, settings.hold_current[C_AXIS]/100.0);
#endif
}
#endif

View File

@ -0,0 +1,32 @@
/*
grbl_trinamic.h - Support for TMC2130 Stepper Drivers SPI Mode
Part of Grbl_ESP32
Copyright (c) 2019 Barton Dring for Buildlog.net LLC
GrblESP32 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 GRBL_TRINAMIC_h
#define GRBL_TRINAMIC_h
#include "grbl.h"
#ifdef USE_TRINAMIC
#include <TMCStepper.h> // https://github.com/teemuatlut/TMCStepper
void Trinamic_Init();
void trinamic_change_settings();
#endif
#endif

View File

@ -0,0 +1,236 @@
/*
unipolar.cpp
Part of Grbl_ESP32
copyright (c) 2019 - Bart Dring. This file was intended for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
Unipolar Class
This class allows you to control a unipolar motor. Unipolar motors have 5 wires. One
is typically tied to a voltage, while the other 4 are switched to ground in a
sequence
To take a step simply call the step(step, direction) function.
*/
#include "grbl.h"
#ifdef USE_UNIPOLAR
// assign the I/O pins used for each coil of the motors
#ifdef X_UNIPOLAR
Unipolar X_Unipolar(X_PIN_PHASE_0, X_PIN_PHASE_1, X_PIN_PHASE_2, X_PIN_PHASE_3, true);
#endif
#ifdef Y_UNIPOLAR
Unipolar Y_Unipolar(Y_PIN_PHASE_0, Y_PIN_PHASE_1, Y_PIN_PHASE_2, Y_PIN_PHASE_3, true);
#endif
#ifdef Z_UNIPOLAR
Unipolar Z_Unipolar(Z_PIN_PHASE_0, Z_PIN_PHASE_1, Z_PIN_PHASE_2, Z_PIN_PHASE_3, true);
#endif
void unipolar_init(){
#ifdef X_UNIPOLAR
X_Unipolar.init();
grbl_send(CLIENT_SERIAL, "[MSG:X Unipolar]\r\n");
#endif
#ifdef Y_UNIPOLAR
Y_Unipolar.init();
grbl_send(CLIENT_SERIAL, "[MSG:Y Unipolar]\r\n");
#endif
#ifdef Z_UNIPOLAR
Z_Unipolar.init();
grbl_send(CLIENT_SERIAL, "[MSG:Z Unipolar]\r\n");
#endif
}
void unipolar_step(uint8_t step_mask, uint8_t dir_mask)
{
#ifdef X_UNIPOLAR
X_Unipolar.step(step_mask & (1<<X_AXIS), dir_mask & (1<<X_AXIS));
#endif
#ifdef Y_UNIPOLAR
Y_Unipolar.step(step_mask & (1<<Y_AXIS), dir_mask & (1<<Y_AXIS));
#endif
#ifdef Z_UNIPOLAR
Z_Unipolar.step(step_mask & (1<<Z_AXIS), dir_mask & (1<<ZX_AXIS));
#endif
}
void unipolar_disable(bool disable)
{
#ifdef X_UNIPOLAR
X_Unipolar.set_enabled(!disable);
#endif
#ifdef Y_UNIPOLAR
Y_Unipolar.set_enabled(!disable);
#endif
#ifdef Z_UNIPOLAR
Z_Unipolar.set_enabled(!disable);
#endif
}
Unipolar::Unipolar(uint8_t pin_phase0, uint8_t pin_phase1, uint8_t pin_phase2, uint8_t pin_phase3, bool half_step) // constructor
{
_pin_phase0 = pin_phase0;
_pin_phase1 = pin_phase1;
_pin_phase2 = pin_phase2;
_pin_phase3 = pin_phase3;
_half_step = half_step;
}
void Unipolar::init() {
pinMode(_pin_phase0, OUTPUT);
pinMode(_pin_phase1, OUTPUT);
pinMode(_pin_phase2, OUTPUT);
pinMode(_pin_phase3, OUTPUT);
_current_phase = 0;
set_enabled(false);
}
void Unipolar::set_enabled(bool enabled)
{
if (enabled == _enabled)
return; // no change
//grbl_sendf(CLIENT_SERIAL, "[MSG:Enabled...%d]\r\n", enabled);
_enabled = enabled;
if (!enabled) {
digitalWrite(_pin_phase0, 0);
digitalWrite(_pin_phase1, 0);
digitalWrite(_pin_phase2, 0);
digitalWrite(_pin_phase3, 0);
}
}
/*
To take a step set step to true and set the driection
step is included so that st.step_outbits can be used to determine if a
step is required on this axis
*/
void Unipolar::step(bool step, bool dir_forward)
{
uint8_t _phase[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // temporary phase values...all start as off
uint8_t phase_max;
if (_half_step)
phase_max = 7;
else
phase_max = 3;
if (!step)
return; // a step is not required on this interrupt
if (!_enabled)
return; // don't do anything, phase is not changed or lost
if (dir_forward) { // 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 (Usual application)
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]);
}
#endif

View File

@ -0,0 +1,54 @@
/*
grbl_unipolar.h
Part of Grbl_ESP32
copyright (c) 2019 - Bart Dring. This file was intended for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
Unipolar Class
This class allows you to control a unipolar motor. Unipolar motors have 5 wires. One
is typically tied to a voltage, while the other 4 are switched to ground in a
sequence
To take a step simply call the step(direction) function. It will take
*/
#ifndef grbl_unipolar_h
#define grbl_unipolar_h
void unipolar_init();
void unipolar_step(uint8_t step_mask, uint8_t dir_mask);
void unipolar_disable(bool enable);
class Unipolar{
public:
Unipolar(uint8_t pin_phase0, uint8_t pin_phase1, uint8_t pin_phase2, uint8_t pin_phase3, bool half_step); // constructor
void set_enabled(bool enabled);
void step(bool step, bool dir_forward);
void init();
private:
uint8_t _current_phase = 0;
bool _enabled = false;
bool _half_step = true; // default is half step, full step
uint8_t _pin_phase0;
uint8_t _pin_phase1;
uint8_t _pin_phase2;
uint8_t _pin_phase3;
};
#endif

View File

@ -0,0 +1,108 @@
/*
inputbuffer.cpp - inputbuffer functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef ARDUINO_ARCH_ESP32
#include "config.h"
#include "inputbuffer.h"
InputBuffer inputBuffer;
InputBuffer::InputBuffer(){
_RXbufferSize = 0;
_RXbufferpos = 0;
}
InputBuffer::~InputBuffer(){
_RXbufferSize = 0;
_RXbufferpos = 0;
}
void InputBuffer::begin(){
_RXbufferSize = 0;
_RXbufferpos = 0;
}
void InputBuffer::end(){
_RXbufferSize = 0;
_RXbufferpos = 0;
}
InputBuffer::operator bool() const
{
return true;
}
int InputBuffer::available(){
return _RXbufferSize;
}
size_t InputBuffer::write(uint8_t c)
{
//No need currently
//keep for compatibility
return 1;
}
size_t InputBuffer::write(const uint8_t *buffer, size_t size)
{
//No need currently
//keep for compatibility
return size;
}
int InputBuffer::peek(void){
if (_RXbufferSize > 0)return _RXbuffer[_RXbufferpos];
else return -1;
}
bool InputBuffer::push (const char * data){
int data_size = strlen(data);
if ((data_size + _RXbufferSize) <= RXBUFFERSIZE){
int current = _RXbufferpos + _RXbufferSize;
if (current > RXBUFFERSIZE) current = current - RXBUFFERSIZE;
for (int i = 0; i < data_size; i++){
if (current > (RXBUFFERSIZE-1)) current = 0;
_RXbuffer[current] = data[i];
current ++;
}
_RXbufferSize+=strlen(data);
return true;
}
return false;
}
int InputBuffer::read(void){
if (_RXbufferSize > 0) {
int v = _RXbuffer[_RXbufferpos];
_RXbufferpos++;
if (_RXbufferpos > (RXBUFFERSIZE-1))_RXbufferpos = 0;
_RXbufferSize--;
return v;
} else return -1;
}
void InputBuffer::flush(void){
//No need currently
//keep for compatibility
}
#endif // ARDUINO_ARCH_ESP32

View File

@ -0,0 +1,71 @@
/*
inputbuffer.h - inputbuffer functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _INPUT_BUFFER_H_
#define _INPUT_BUFFER_H_
#include "Print.h"
#define RXBUFFERSIZE 128
class InputBuffer: public Print{
public:
InputBuffer();
~InputBuffer();
size_t write(uint8_t c);
size_t write(const uint8_t *buffer, size_t size);
inline size_t write(const char * s)
{
return write((uint8_t*) s, strlen(s));
}
inline size_t write(unsigned long n)
{
return write((uint8_t) n);
}
inline size_t write(long n)
{
return write((uint8_t) n);
}
inline size_t write(unsigned int n)
{
return write((uint8_t) n);
}
inline size_t write(int n)
{
return write((uint8_t) n);
}
void begin();
void end();
int available();
int peek(void);
int read(void);
bool push (const char * data);
void flush(void);
operator bool() const;
private:
uint8_t _RXbuffer[RXBUFFERSIZE];
uint16_t _RXbufferSize;
uint16_t _RXbufferpos;
};
extern InputBuffer inputBuffer;
#endif

View File

@ -0,0 +1,53 @@
/*
jog.h - Jogging methods
Part of Grbl
Copyright (c) 2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
// Sets up valid jog motion received from g-code parser, checks for soft-limits, and executes the jog.
uint8_t jog_execute(plan_line_data_t *pl_data, parser_block_t *gc_block)
{
// Initialize planner data struct for jogging motions.
// NOTE: Spindle and coolant are allowed to fully function with overrides during a jog.
pl_data->feed_rate = gc_block->values.f;
pl_data->condition |= PL_COND_FLAG_NO_FEED_OVERRIDE;
#ifdef USE_LINE_NUMBERS
pl_data->line_number = gc_block->values.n;
#endif
if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) {
if (system_check_travel_limits(gc_block->values.xyz)) { return(STATUS_TRAVEL_EXCEEDED); }
}
// Valid jog command. Plan, set state, and execute.
mc_line(gc_block->values.xyz,pl_data);
if (sys.state == STATE_IDLE) {
if (plan_get_current_block() != NULL) { // Check if there is a block to execute.
sys.state = STATE_JOG;
st_prep_buffer();
st_wake_up(); // NOTE: Manual start. No state machine required.
}
}
return(STATUS_OK);
}

View File

@ -0,0 +1,35 @@
/*
jog.h - Jogging methods
Part of Grbl
Copyright (c) 2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 jog_h
#define jog_h
#include "grbl.h"
// System motion line numbers must be zero.
#define JOG_LINE_NUMBER 0
// Sets up valid jog motion received from g-code parser, checks for soft-limits, and executes the jog.
uint8_t jog_execute(plan_line_data_t *pl_data, parser_block_t *gc_block);
#endif

View File

@ -0,0 +1,483 @@
/*
motion_control.c - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
uint8_t ganged_mode = SQUARING_MODE_DUAL;
// this allows kinematics to be used.
void mc_line_kins(float *target, plan_line_data_t *pl_data, float *position)
{
#ifndef USE_KINEMATICS
mc_line(target, pl_data);
#else // else use kinematics
inverse_kinematics(target, pl_data, position);
#endif
}
// Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second
// unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in
// (1 minute)/feed_rate time.
// NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line
// segments, must pass through this routine before being passed to the planner. The seperation of
// mc_line and plan_buffer_line is done primarily to place non-planner-type functions from being
// in the planner and to let backlash compensation or canned cycle integration simple and direct.
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
// from everywhere in Grbl.
if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) {
// 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 in check gcode mode, prevent motion by blocking planner. Soft limits still work.
if (sys.state == STATE_CHECK_MODE) { return; }
// NOTE: Backlash compensation may be installed here. It will need direction info to track when
// to insert a backlash line motion(s) before the intended line motion and will require its own
// plan_check_full_buffer() and check for system abort loop. Also for position reporting
// backlash steps will need to be also tracked, which will need to be kept at a system level.
// There are likely some other things that will need to be tracked as well. However, we feel
// that backlash compensation should NOT be handled by Grbl itself, because there are a myriad
// of ways to implement it and can be effective or ineffective for different CNC machines. This
// would be better handled by the interface as a post-processor task, where the original g-code
// is translated and inserts backlash motions that best suits the machine.
// NOTE: Perhaps as a middle-ground, all that needs to be sent is a flag or special command that
// indicates to Grbl what is a backlash compensation motion, so that Grbl executes the move but
// doesn't update the machine position values. Since the position values used by the g-code
// parser and planner are separate from the system machine positions, this is doable.
// If the buffer is full: good! That means we are well ahead of the robot.
// Remain in this loop until there is room in the buffer.
do {
protocol_execute_realtime(); // Check for any run-time commands
if (sys.abort) { return; } // Bail, if system abort.
if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full.
else { break; }
} while (1);
// Plan and queue motion into planner buffer
// uint8_t plan_status; // Not used in normal operation.
plan_buffer_line(target, pl_data);
}
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// offset == offset from current xyz, axis_X defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used
// for vector transformation direction.
// 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
// 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,
uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc)
{
float center_axis0 = position[axis_0] + offset[axis_0];
float center_axis1 = position[axis_1] + offset[axis_1];
float r_axis0 = -offset[axis_0]; // Radius vector from center to current location
float r_axis1 = -offset[axis_1];
float rt_axis0 = target[axis_0] - center_axis0;
float rt_axis1 = target[axis_1] - center_axis1;
#ifdef USE_KINEMATICS
float previous_position[N_AXIS];
uint16_t n;
for (n = 0; n < N_AXIS; n++) {
previous_position[n] = position[n];
}
#endif
// CCW angle between position and target from circle center. Only one atan2() trig computation required.
float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1);
if (is_clockwise_arc) { // Correct atan2 output per direction
if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel -= 2*M_PI; }
} else {
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
// (2x) settings.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.
// 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)/
sqrt(settings.arc_tolerance*(2*radius - settings.arc_tolerance)) );
if (segments) {
// 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
// all segments.
if (pl_data->condition & PL_COND_FLAG_INVERSE_TIME) {
pl_data->feed_rate *= segments;
bit_false(pl_data->condition,PL_COND_FLAG_INVERSE_TIME); // Force as feed absolute mode over arc segments.
}
float theta_per_segment = angular_travel/segments;
float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments;
/* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
and phi is the angle of rotation. Solution approach by Jens Geisler.
r_T = [cos(phi) -sin(phi);
sin(phi) cos(phi] * r ;
For arc generation, the center of the circle is the axis of rotation and the radius vector is
defined from the circle center to the initial position. Each line segment is formed by successive
vector rotations. Single precision values can accumulate error greater than tool precision in rare
cases. So, exact arc path correction is implemented. This approach avoids the problem of too many very
expensive trig operations [sin(),cos(),tan()] which can take 100-200 usec each to compute.
Small angle approximation may be used to reduce computation overhead further. A third-order approximation
(second order sin() has too much error) holds for most, if not, all CNC applications. Note that this
approximation will begin to accumulate a numerical drift error when theta_per_segment is greater than
~0.25 rad(14 deg) AND the approximation is successively used without correction several dozen times. This
scenario is extremely unlikely, since segment lengths and theta_per_segment are automatically generated
and scaled by the arc tolerance setting. Only a very large arc tolerance setting, unrealistic for CNC
applications, would cause this numerical drift error. However, it is best to set N_ARC_CORRECTION from a
low of ~4 to a high of ~20 or so to avoid trig operations while keeping arc generation accurate.
This approximation also allows mc_arc to immediately insert a line segment into the planner
without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied
a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead.
This is important when there are successive arc motions.
*/
// Computes: cos_T = 1 - theta_per_segment^2/2, sin_T = theta_per_segment - theta_per_segment^3/6) in ~52usec
float cos_T = 2.0 - theta_per_segment*theta_per_segment;
float sin_T = theta_per_segment*0.16666667*(cos_T + 4.0);
cos_T *= 0.5;
float sin_Ti;
float cos_Ti;
float r_axisi;
uint16_t i;
uint8_t count = 0;
for (i = 1; i<segments; i++) { // Increment (segments-1).
if (count < N_ARC_CORRECTION) {
// Apply vector rotation matrix. ~40 usec
r_axisi = r_axis0*sin_T + r_axis1*cos_T;
r_axis0 = r_axis0*cos_T - r_axis1*sin_T;
r_axis1 = r_axisi;
count++;
} else {
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments. ~375 usec
// Compute exact location by applying transformation matrix from initial radius vector(=-offset).
cos_Ti = cos(i*theta_per_segment);
sin_Ti = sin(i*theta_per_segment);
r_axis0 = -offset[axis_0]*cos_Ti + offset[axis_1]*sin_Ti;
r_axis1 = -offset[axis_0]*sin_Ti - offset[axis_1]*cos_Ti;
count = 0;
}
// Update arc_target location
position[axis_0] = center_axis0 + r_axis0;
position[axis_1] = center_axis1 + r_axis1;
position[axis_linear] += linear_per_segment;
#ifdef USE_KINEMATICS
mc_line_kins(position, pl_data, previous_position);
previous_position[axis_0] = position[axis_0];
previous_position[axis_1] = position[axis_1];
previous_position[axis_linear] = position[axis_linear];
#else
mc_line(position, pl_data);
#endif
// Bail mid-circle on system abort. Runtime command check already performed by mc_line.
if (sys.abort) { return; }
}
}
// Ensure last segment arrives at target location.
#ifdef USE_KINEMATICS
mc_line_kins(target, pl_data, previous_position);
#else
mc_line(target, pl_data);
#endif
}
// Execute dwell in seconds.
void mc_dwell(float seconds)
{
if (sys.state == STATE_CHECK_MODE) { return; }
protocol_buffer_synchronize();
delay_sec(seconds, DELAY_MODE_DWELL);
}
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command.
// NOTE: There should be no motions in the buffer and Grbl must be in an idle state before
// executing the homing cycle. This prevents incorrect buffered plans after homing.
void mc_homing_cycle(uint8_t cycle_mask)
{
#ifdef USE_CUSTOM_HOMING
if (user_defined_homing())
{
return;
}
#endif
// This give kinematics a chance to do something before normal homing
// if it returns true, the homing is canceled.
#ifdef USE_KINEMATICS
if (kinematics_pre_homing(cycle_mask)) {
return;
}
#endif
// Check and abort homing cycle, if hard limits are already enabled. Helps prevent problems
// with machines with limits wired on both ends of travel to one limit pin.
// TODO: Move the pin-specific LIMIT_PIN call to limits.c as a function.
#ifdef LIMITS_TWO_SWITCHES_ON_AXES
if (limits_get_state()) {
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT);
return;
}
#endif
limits_disable(); // Disable hard limits pin change register for cycle duration
// -------------------------------------------------------------------------------------
// Perform homing routine. NOTE: Special motion case. Only system reset works.
#ifdef HOMING_SINGLE_AXIS_COMMANDS
if (cycle_mask) { limits_go_home(cycle_mask); } // Perform homing cycle based on mask.
else
#endif
{
// Search to engage all axes limit switches at faster homing seek rate.
if (! axis_is_squared(HOMING_CYCLE_0))
limits_go_home(HOMING_CYCLE_0); // Homing cycle 0
else {
ganged_mode = SQUARING_MODE_DUAL;
limits_go_home(HOMING_CYCLE_0);
ganged_mode = SQUARING_MODE_A;
limits_go_home(HOMING_CYCLE_0);
ganged_mode = SQUARING_MODE_B;
limits_go_home(HOMING_CYCLE_0);
ganged_mode = SQUARING_MODE_DUAL; // always return to dual
}
#ifdef HOMING_CYCLE_1
if (! axis_is_squared(HOMING_CYCLE_1))
limits_go_home(HOMING_CYCLE_1);
else {
ganged_mode = SQUARING_MODE_DUAL;
limits_go_home(HOMING_CYCLE_1);
ganged_mode = SQUARING_MODE_A;
limits_go_home(HOMING_CYCLE_1);
ganged_mode = SQUARING_MODE_B;
limits_go_home(HOMING_CYCLE_1);
ganged_mode = SQUARING_MODE_DUAL; // always return to dual
}
#endif
#ifdef HOMING_CYCLE_2
if (! axis_is_squared(HOMING_CYCLE_2))
limits_go_home(HOMING_CYCLE_2);
else {
ganged_mode = SQUARING_MODE_DUAL;
limits_go_home(HOMING_CYCLE_2);
ganged_mode = SQUARING_MODE_A;
limits_go_home(HOMING_CYCLE_2);
ganged_mode = SQUARING_MODE_B;
limits_go_home(HOMING_CYCLE_2);
ganged_mode = SQUARING_MODE_DUAL; // always return to dual
}
#endif
#ifdef HOMING_CYCLE_3
limits_go_home(HOMING_CYCLE_3); // Homing cycle 3
#endif
#ifdef HOMING_CYCLE_4
limits_go_home(HOMING_CYCLE_4); // Homing cycle 4
#endif
#ifdef HOMING_CYCLE_5
limits_go_home(HOMING_CYCLE_5); // Homing cycle 5
#endif
}
protocol_execute_realtime(); // Check for reset and set system abort.
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
// Homing cycle complete! Setup system for normal operation.
// -------------------------------------------------------------------------------------
// Sync gcode parser and planner positions to homed position.
gc_sync_position();
plan_sync_position();
#ifdef USE_KINEMATICS
// This give kinematics a chance to do something after normal homing
kinematics_post_homing();
#endif
// If hard limits feature enabled, re-enable hard limits pin change register after homing cycle.
limits_init();
}
// Perform tool length probe cycle. Requires probe switch.
// NOTE: Upon probe failure, the program will be stopped and placed into ALARM state.
uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_flags)
{
// TODO: Need to update this cycle so it obeys a non-auto cycle start.
if (sys.state == STATE_CHECK_MODE) { return(GC_PROBE_CHECK_MODE); }
// Finish all queued commands and empty planner buffer before starting probe cycle.
protocol_buffer_synchronize();
if (sys.abort) { return(GC_PROBE_ABORT); } // Return if system reset has been issued.
// Initialize probing control variables
uint8_t is_probe_away = bit_istrue(parser_flags,GC_PARSER_PROBE_IS_AWAY);
uint8_t is_no_error = bit_istrue(parser_flags,GC_PARSER_PROBE_IS_NO_ERROR);
sys.probe_succeeded = false; // Re-initialize probe history before beginning cycle.
probe_configure_invert_mask(is_probe_away);
// After syncing, check if probe is already triggered. If so, halt and issue alarm.
// NOTE: This probe initialization error applies to all probing cycles.
if ( probe_get_state() ) { // Check probe pin state.
system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_INITIAL);
protocol_execute_realtime();
probe_configure_invert_mask(false); // Re-initialize invert mask before returning.
return(GC_PROBE_FAIL_INIT); // Nothing else to do but bail.
}
// Setup and queue probing motion. Auto cycle-start should not start the cycle.
mc_line(target, pl_data);
// Activate the probing state monitor in the stepper module.
sys_probe_state = PROBE_ACTIVE;
// Perform probing cycle. Wait here until probe is triggered or motion completes.
system_set_exec_state_flag(EXEC_CYCLE_START);
do {
protocol_execute_realtime();
if (sys.abort) { return(GC_PROBE_ABORT); } // Check for system abort
} while (sys.state != STATE_IDLE);
// Probing cycle complete!
// Set state variables and error out, if the probe failed and cycle with error is enabled.
if (sys_probe_state == PROBE_ACTIVE) {
if (is_no_error) { memcpy(sys_probe_position, sys_position, sizeof(sys_position)); }
else { system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_CONTACT); }
} else {
sys.probe_succeeded = true; // Indicate to system the probing cycle completed successfully.
}
sys_probe_state = PROBE_OFF; // Ensure probe state monitor is disabled.
probe_configure_invert_mask(false); // Re-initialize invert mask.
protocol_execute_realtime(); // Check and execute run-time commands
// Reset the stepper and planner buffers to remove the remainder of the probe motion.
st_reset(); // Reset step segment buffer.
plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared.
plan_sync_position(); // Sync planner position to current machine position.
#ifdef MESSAGE_PROBE_COORDINATES
// All done! Output the probe position as message.
report_probe_parameters(CLIENT_ALL);
#endif
if (sys.probe_succeeded) { return(GC_PROBE_FOUND); } // Successful probe cycle.
else { return(GC_PROBE_FAIL_END); } // Failed to trigger probe within travel. With or without error.
}
// Plans and executes the single special motion case for parking. Independent of main planner buffer.
// NOTE: Uses the always free planner ring buffer head to store motion parameters for execution.
void mc_parking_motion(float *parking_target, plan_line_data_t *pl_data)
{
if (sys.abort) { return; } // Block during abort.
uint8_t plan_status = plan_buffer_line(parking_target, pl_data);
if (plan_status) {
bit_true(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION);
bit_false(sys.step_control, STEP_CONTROL_END_MOTION); // Allow parking motion to execute, if feed hold is active.
st_parking_setup_buffer(); // Setup step segment buffer for special parking motion case
st_prep_buffer();
st_wake_up();
do {
protocol_exec_rt_system();
if (sys.abort) { return; }
} while (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION);
st_parking_restore_buffer(); // Restore step segment buffer to normal run state.
} else {
bit_false(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION);
protocol_exec_rt_system();
}
}
// Method to ready the system to reset by setting the realtime reset command and killing any
// active processes in the system. This also checks if a system reset is issued while Grbl
// is in a motion state. If so, kills the steppers and sets the system alarm to flag position
// lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by
// realtime abort command and hard limits. So, keep to a minimum.
void mc_reset()
{
// Only this function can set the system reset. Helps prevent multiple kill calls.
if (bit_isfalse(sys_rt_exec_state, EXEC_RESET)) {
system_set_exec_state_flag(EXEC_RESET);
// Kill spindle and coolant.
spindle_stop();
coolant_stop();
// turn off all digital I/O
sys_io_control(0xFF, false);
#ifdef ENABLE_SD_CARD
// do we need to stop a running SD job?
if (get_sd_state(false) == SDCARD_BUSY_PRINTING) {
//Report print stopped
report_feedback_message(MESSAGE_SD_FILE_QUIT);
closeFile();
}
#endif
// Kill steppers only if in any motion state, i.e. cycle, actively holding, or homing.
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps
// the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
// violated, by which, all bets are off.
if ((sys.state & (STATE_CYCLE | STATE_HOMING | STATE_JOG)) ||
(sys.step_control & (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION))) {
if (sys.state == STATE_HOMING) {
if (!sys_rt_exec_alarm) {system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
} else { system_set_exec_alarm(EXEC_ALARM_ABORT_CYCLE); }
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
#endif
}
}

View File

@ -0,0 +1,74 @@
/*
motion_control.h - high level interface for issuing motion commands
Part of Grbl
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 motion_control_h
#define motion_control_h
#include "grbl.h"
// System motion commands must have a line number of zero.
#define HOMING_CYCLE_LINE_NUMBER 0
#define PARKING_MOTION_LINE_NUMBER 0
#define HOMING_CYCLE_ALL 0 // Must be zero.
#define HOMING_CYCLE_X bit(X_AXIS)
#define HOMING_CYCLE_Y bit(Y_AXIS)
#define HOMING_CYCLE_Z bit(Z_AXIS)
#define HOMING_CYCLE_A bit(A_AXIS)
#define HOMING_CYCLE_B bit(B_AXIS)
#define HOMING_CYCLE_C bit(C_AXIS)
// Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second
// unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in
// (1 minute)/feed_rate time.
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);
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, is_clockwise_arc boolean. Used
// for vector transformation direction.
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);
// Dwell for a specific number of seconds
void mc_dwell(float seconds);
// Perform homing cycle to locate machine zero. Requires limit switches.
void mc_homing_cycle(uint8_t cycle_mask);
// Perform tool length probe cycle. Requires probe switch.
uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_flags);
// Plans and executes the single special motion case for parking. Independent of main planner buffer.
void mc_parking_motion(float *parking_target, plan_line_data_t *pl_data);
// Performs system reset. If in motion state, kills all motion and sets system alarm.
void mc_reset();
#endif

View File

@ -0,0 +1,452 @@
/*
nofile.h - ESP3D data file
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//data generated by https://github.com/AraHaan/bin2c
//bin2c Conversion Tool v0.14.0 - Windows - [FINAL].
#ifndef __nofile_h
#define __nofile_h
/* Generated by bin2c, do not edit manually */
/* Contents of file tool.html.gz */
#define PAGE_NOFILES_SIZE 6728
const char PAGE_NOFILES[6728] PROGMEM = {
0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xED, 0x3C, 0x89, 0x72, 0xDB, 0xC6,
0x92, 0xBF, 0x82, 0x20, 0x15, 0x93, 0x58, 0x02, 0x24, 0x2E, 0xDE, 0xA2, 0xBC, 0x49, 0x2C, 0x27,
0xDA, 0xB2, 0x63, 0x97, 0x24, 0xAF, 0xF7, 0x95, 0xE3, 0x52, 0x81, 0xC4, 0x50, 0xC4, 0x1A, 0x04,
0x28, 0x60, 0x28, 0x4A, 0x96, 0xB9, 0xDF, 0xBE, 0xDD, 0x3D, 0x83, 0x8B, 0x97, 0x8E, 0xE7, 0xB7,
0x2F, 0x5B, 0xF5, 0xA2, 0x90, 0x00, 0xE6, 0xE8, 0xE9, 0xE9, 0xBB, 0x1B, 0x43, 0x1F, 0xCD, 0xF8,
0x3C, 0x3C, 0x3E, 0x9A, 0x31, 0xCF, 0x3F, 0x3E, 0x4A, 0xF9, 0x5D, 0xC8, 0x8E, 0xB1, 0xE5, 0x7E,
0x1A, 0x47, 0xDC, 0x98, 0x7A, 0xF3, 0x20, 0xBC, 0x1B, 0xA4, 0x5E, 0x94, 0x1A, 0x29, 0x4B, 0x82,
0xE9, 0xD0, 0x98, 0xA7, 0x06, 0x67, 0xB7, 0xDC, 0x48, 0x83, 0xAF, 0xCC, 0xF0, 0xFC, 0xFF, 0x5E,
0xA6, 0x7C, 0x60, 0x99, 0xE6, 0x4F, 0x43, 0x63, 0xC5, 0xC6, 0x5F, 0x02, 0xBE, 0xA7, 0x97, 0xC0,
0x61, 0x2B, 0x3C, 0x2E, 0x6E, 0xD7, 0xE3, 0xD8, 0xBF, 0xAB, 0x2C, 0xA1, 0xFE, 0xCE, 0xC2, 0x1B,
0xC6, 0x83, 0x89, 0xA7, 0xFC, 0xC1, 0x96, 0x4C, 0xD5, 0xF3, 0x67, 0xFD, 0xE7, 0x24, 0xF0, 0x42,
0xBD, 0x84, 0x43, 0x09, 0x96, 0xBB, 0xB8, 0x1D, 0x86, 0x41, 0xC4, 0x8C, 0x19, 0x0B, 0xAE, 0x66,
0xB0, 0x56, 0xD3, 0xB5, 0x7B, 0xED, 0xAE, 0xE5, 0x3A, 0xC3, 0x49, 0x1C, 0xC6, 0xC9, 0xE0, 0x47,
0xC7, 0x71, 0x86, 0x63, 0x6F, 0xF2, 0xE5, 0x2A, 0x89, 0x97, 0x91, 0x6F, 0xC8, 0xD6, 0xE9, 0x74,
0xBA, 0xE6, 0xDE, 0x38, 0x64, 0xF7, 0xE3, 0x38, 0xF1, 0x59, 0x32, 0x30, 0x87, 0xE2, 0xC6, 0x48,
0x17, 0xDE, 0x24, 0x88, 0xAE, 0xA0, 0x61, 0xEE, 0xDD, 0x1A, 0xAB, 0xC0, 0xE7, 0x33, 0xDA, 0xC1,
0x9A, 0xFB, 0xF7, 0xAB, 0x59, 0xC0, 0x19, 0x8D, 0x60, 0x83, 0x28, 0x5E, 0x25, 0xDE, 0x62, 0xB8,
0xF0, 0x7C, 0x1F, 0x87, 0xDB, 0xF3, 0xF9, 0x9A, 0xCF, 0xEE, 0x69, 0xF3, 0x5E, 0x18, 0x5C, 0x45,
0x83, 0x90, 0x4D, 0xF9, 0xBA, 0x49, 0x8B, 0x1C, 0x73, 0xDC, 0xEF, 0x31, 0x4F, 0x8E, 0xB9, 0xAF,
0x6F, 0x35, 0xCD, 0xF2, 0x26, 0x62, 0x42, 0x75, 0x54, 0xDE, 0x34, 0xBB, 0xCF, 0x96, 0xEA, 0xED,
0xDF, 0xF3, 0x0D, 0x4B, 0x90, 0x64, 0xA1, 0x44, 0x81, 0xC7, 0x8B, 0x6C, 0x5B, 0x70, 0x3B, 0xB0,
0x16, 0xB7, 0x4A, 0x1A, 0x87, 0x81, 0xAF, 0xFC, 0xE8, 0xFB, 0xBE, 0xC4, 0xCD, 0x48, 0x79, 0x12,
0x2C, 0x98, 0x9F, 0x23, 0x34, 0x88, 0xF8, 0xCC, 0x88, 0xA7, 0x06, 0xBF, 0x5B, 0xB0, 0x7A, 0xEC,
0xFB, 0xDA, 0xFD, 0x0E, 0xF2, 0xF5, 0xF1, 0x6F, 0xED, 0xDD, 0x2F, 0xE2, 0x34, 0xE0, 0x41, 0x1C,
0x0D, 0x12, 0x16, 0x7A, 0x3C, 0xB8, 0x61, 0x43, 0x3F, 0x48, 0x17, 0xA1, 0x77, 0x37, 0x18, 0x87,
0xF1, 0xE4, 0x4B, 0x4E, 0x1E, 0x64, 0xBA, 0x62, 0xB5, 0x01, 0x73, 0xA2, 0x90, 0xCF, 0x26, 0x71,
0xE2, 0xD1, 0xC4, 0x28, 0x8E, 0x58, 0xC6, 0xAB, 0xC9, 0x64, 0xB2, 0x6E, 0x7A, 0x13, 0x84, 0x73,
0x5F, 0x30, 0x6A, 0x07, 0xFB, 0x4C, 0xD3, 0xCC, 0x06, 0x2A, 0x9E, 0xEE, 0x0D, 0xA6, 0xF1, 0x64,
0x99, 0xC2, 0x75, 0x16, 0x03, 0x05, 0x4A, 0x53, 0xD7, 0xCD, 0x85, 0x17, 0xB1, 0xF0, 0x7E, 0xEE,
0x25, 0x57, 0x41, 0x64, 0x8C, 0x63, 0xCE, 0xE3, 0xF9, 0xC0, 0x06, 0x64, 0x76, 0xCB, 0x84, 0xA4,
0xD6, 0x06, 0xA5, 0x32, 0x1A, 0x26, 0x9E, 0x1F, 0x2C, 0xD3, 0x01, 0xCA, 0x5C, 0x26, 0xEC, 0xE3,
0xF8, 0xD6, 0x48, 0x67, 0x9E, 0x1F, 0xAF, 0x06, 0xA6, 0x82, 0xB3, 0xF0, 0x93, 0x5C, 0x8D, 0xBD,
0xBA, 0xA9, 0xE3, 0x5F, 0xD3, 0x6C, 0x6B, 0xC3, 0xC7, 0x0C, 0x92, 0x98, 0x1A, 0xA4, 0x18, 0x39,
0xD5, 0x80, 0x60, 0x59, 0x07, 0x0A, 0x02, 0xB4, 0xDD, 0x6F, 0x53, 0xF4, 0xB0, 0xA0, 0xB7, 0xF1,
0x2F, 0xDB, 0x81, 0x6C, 0x2C, 0xED, 0x09, 0xE4, 0xC2, 0x48, 0x50, 0x8C, 0xB2, 0xDD, 0x39, 0x48,
0x9B, 0xA2, 0x0F, 0xA5, 0x78, 0x47, 0x97, 0xA4, 0xE4, 0xA6, 0x44, 0x4D, 0xE3, 0x64, 0x0E, 0x8B,
0x44, 0x3C, 0x89, 0xC3, 0xFB, 0xAA, 0x24, 0x08, 0x4D, 0xF2, 0x96, 0x3C, 0x1E, 0x4A, 0xB9, 0x75,
0x90, 0x90, 0xD9, 0x76, 0x3A, 0xB8, 0x1B, 0x1B, 0x1A, 0x9E, 0xA4, 0xDC, 0xED, 0x76, 0x7B, 0x1F,
0x23, 0x8B, 0xD6, 0x60, 0xEE, 0x5D, 0x31, 0x21, 0x67, 0xDB, 0xEC, 0x05, 0x91, 0x7B, 0x1C, 0x7B,
0x83, 0x28, 0x65, 0x5C, 0xD9, 0xC3, 0xBF, 0x6E, 0x95, 0xCB, 0x0F, 0x8E, 0x35, 0x62, 0x83, 0x27,
0x60, 0xD0, 0x84, 0xEE, 0x94, 0x99, 0xA3, 0x30, 0x2F, 0x65, 0x06, 0xC8, 0x6A, 0xBC, 0xE4, 0x4A,
0xD3, 0x6A, 0xA7, 0x7A, 0x01, 0x77, 0xAB, 0xAF, 0x4A, 0x70, 0xA1, 0x05, 0xF7, 0x55, 0x56, 0x77,
0x3A, 0xDE, 0x94, 0xF5, 0x87, 0x30, 0x03, 0x29, 0x09, 0x56, 0xED, 0x19, 0x5B, 0xD3, 0x4D, 0xE8,
0xEC, 0x65, 0x1D, 0x96, 0x69, 0xEB, 0x56, 0xB7, 0xAD, 0xDB, 0x8E, 0xA3, 0x37, 0x3B, 0x9A, 0xC4,
0x01, 0x69, 0xBD, 0xD8, 0xD0, 0x33, 0x21, 0xBE, 0x63, 0x1E, 0xE5, 0xA2, 0x10, 0x44, 0xC4, 0x4F,
0x21, 0x11, 0xD5, 0xC1, 0xA6, 0xE0, 0xFC, 0x4A, 0xB0, 0xDA, 0x35, 0xCD, 0x61, 0xC9, 0x96, 0x4E,
0x58, 0xC4, 0x59, 0xB2, 0x69, 0xDE, 0xE6, 0x81, 0xEF, 0x87, 0x4C, 0xB8, 0xA4, 0x78, 0x39, 0x99,
0x19, 0x68, 0x11, 0x80, 0x9E, 0x73, 0x2F, 0x0A, 0x16, 0xCB, 0x90, 0xEC, 0xCB, 0x70, 0x7F, 0xCF,
0x64, 0x99, 0xA4, 0x40, 0xA2, 0x45, 0x1C, 0x10, 0xF0, 0x47, 0x4A, 0x0C, 0xF1, 0x6D, 0xE1, 0x25,
0x80, 0xD1, 0xF0, 0x80, 0x3F, 0x78, 0xA2, 0x3C, 0xEF, 0x10, 0xC1, 0x79, 0xFC, 0xD5, 0x58, 0xA6,
0xE8, 0x91, 0x58, 0xC8, 0x26, 0x5C, 0xA0, 0x83, 0x7B, 0xDD, 0x6A, 0xDC, 0x6C, 0x20, 0x9A, 0x1B,
0x8B, 0x04, 0xB6, 0x91, 0xDC, 0x1D, 0x36, 0xA4, 0x8E, 0xD3, 0xF5, 0xC6, 0xDD, 0x0D, 0xF3, 0x60,
0xB3, 0x8E, 0xEF, 0xB9, 0x15, 0x28, 0xD2, 0xD8, 0xEA, 0x95, 0x36, 0x61, 0x75, 0x2B, 0x4D, 0x64,
0x80, 0x2B, 0x4D, 0x83, 0x1D, 0x33, 0x07, 0xDB, 0x33, 0xB7, 0x4C, 0xF7, 0x0E, 0x64, 0xED, 0x5E,
0xC7, 0xEC, 0x9B, 0x1B, 0xC8, 0x5A, 0xB6, 0x3D, 0x76, 0x4D, 0x42, 0x36, 0x98, 0x5F, 0xDD, 0x4B,
0xA6, 0xCE, 0xBC, 0x68, 0xD3, 0x6C, 0x77, 0x72, 0xEB, 0x55, 0xD6, 0x7F, 0x72, 0x12, 0x62, 0xAE,
0x44, 0x61, 0x87, 0x3D, 0x31, 0xF1, 0x6F, 0x63, 0xDD, 0xCE, 0x04, 0xFF, 0x9E, 0xAD, 0x4E, 0x28,
0x1F, 0x57, 0x09, 0xBB, 0x7B, 0x8A, 0xD9, 0xA8, 0x4C, 0x24, 0xAC, 0x09, 0xCD, 0xC3, 0xDB, 0x76,
0x4C, 0xA9, 0x84, 0xD9, 0xD8, 0x87, 0xB6, 0xF9, 0xCF, 0xDC, 0x51, 0x08, 0x48, 0x81, 0x86, 0x7C,
0xD1, 0x8B, 0xDB, 0x41, 0x35, 0x1E, 0x20, 0xCF, 0x5F, 0x74, 0x56, 0xA4, 0x06, 0xFB, 0x82, 0x68,
0xB1, 0xE4, 0x9F, 0x30, 0x76, 0x19, 0x4D, 0x83, 0x90, 0x7D, 0x1E, 0x0C, 0xB2, 0xFD, 0xE0, 0xA3,
0xB1, 0x5C, 0x84, 0xB1, 0xE7, 0x1B, 0xE3, 0x25, 0xD8, 0x9C, 0x7F, 0x99, 0xA5, 0xFF, 0x5B, 0xB3,
0x34, 0x3C, 0xA8, 0xDC, 0xED, 0xF1, 0xC4, 0xF4, 0xD9, 0x86, 0x92, 0xB9, 0x9D, 0x71, 0xCF, 0xF7,
0x9E, 0xC4, 0x54, 0xE9, 0x05, 0xFF, 0xC5, 0xDA, 0xBF, 0x0E, 0x6B, 0x1D, 0x6B, 0x6C, 0xFA, 0x9B,
0x31, 0xA8, 0x35, 0xEE, 0xF8, 0xBD, 0xF6, 0xD3, 0x58, 0x2B, 0xB4, 0xFD, 0x5F, 0xAC, 0xFD, 0x8B,
0xB3, 0xD6, 0xEE, 0xF4, 0xBD, 0xF1, 0x24, 0x4B, 0x5C, 0xA6, 0x71, 0x0C, 0x14, 0x39, 0x90, 0xB7,
0x58, 0x5D, 0xB3, 0xB7, 0x0B, 0xF6, 0x23, 0x52, 0x97, 0xAD, 0x04, 0xE4, 0x9F, 0xB0, 0xE4, 0x3C,
0xF6, 0xBD, 0x22, 0xD9, 0x21, 0x92, 0xE5, 0x59, 0xF1, 0x34, 0xB8, 0x65, 0xFE, 0xF0, 0x2B, 0xC4,
0xEC, 0x3E, 0xBB, 0xC5, 0x32, 0x02, 0x48, 0xA2, 0xC4, 0x4A, 0xC0, 0x32, 0x31, 0x15, 0xC5, 0x1C,
0x0B, 0x44, 0x16, 0x1B, 0xCC, 0x61, 0x51, 0x71, 0xC8, 0xF2, 0x24, 0xBA, 0x47, 0xC9, 0x9F, 0x86,
0xE0, 0x52, 0x29, 0x83, 0xDA, 0x99, 0x11, 0x6F, 0xB7, 0x96, 0xDD, 0xAD, 0xAB, 0x49, 0x54, 0x29,
0x5D, 0x00, 0x81, 0xBB, 0xDF, 0x93, 0xE5, 0x59, 0x66, 0x35, 0x03, 0xAC, 0x64, 0x87, 0xE5, 0x4E,
0xA1, 0x6B, 0x7B, 0xE7, 0xCA, 0xEE, 0x7D, 0xD3, 0x07, 0x76, 0x41, 0xC7, 0x3C, 0x0A, 0x2D, 0xE5,
0xC9, 0x98, 0x6F, 0x58, 0x28, 0xF8, 0x66, 0x25, 0x6A, 0xB0, 0xB5, 0xE1, 0x76, 0xCD, 0x41, 0x28,
0xBF, 0x20, 0x4D, 0xC6, 0xF4, 0x1D, 0xE4, 0xF8, 0x71, 0xCA, 0xF0, 0x2F, 0xA3, 0x03, 0x66, 0xD4,
0x25, 0x29, 0xB1, 0xE5, 0x82, 0x99, 0x90, 0x50, 0x34, 0xB4, 0x53, 0x48, 0x6C, 0xFC, 0xDB, 0x97,
0x24, 0x3F, 0x91, 0x7C, 0x95, 0x5C, 0x74, 0x8A, 0x7F, 0x19, 0x7A, 0xD5, 0x4A, 0x80, 0x29, 0xB1,
0xCB, 0x7A, 0x37, 0x45, 0xBC, 0x93, 0x61, 0x2F, 0x85, 0xC6, 0x6D, 0xB6, 0xD9, 0xFC, 0xE9, 0x5B,
0xD9, 0x46, 0xE7, 0xEF, 0xE4, 0xF6, 0xBA, 0x39, 0x0B, 0x7C, 0x76, 0x19, 0xF0, 0x8A, 0x86, 0xAC,
0xFF, 0x7D, 0xCE, 0xFC, 0xC0, 0x53, 0xEA, 0x73, 0xB0, 0xD9, 0x42, 0xE2, 0xBB, 0x1D, 0xE0, 0xB8,
0x76, 0xBF, 0x21, 0xA3, 0xA2, 0xAF, 0xDD, 0x43, 0x48, 0xD9, 0xA4, 0x74, 0x92, 0x30, 0x16, 0x29,
0x10, 0xEA, 0xC2, 0xFC, 0xBC, 0x46, 0xD7, 0xED, 0x74, 0xF7, 0xCE, 0xA7, 0xFA, 0xDD, 0xFA, 0xA8,
0x25, 0xCA, 0x9B, 0x47, 0x3C, 0xE0, 0x70, 0x39, 0x39, 0x7F, 0xEF, 0xBC, 0x52, 0x78, 0x1C, 0x87,
0xCA, 0x02, 0x2C, 0xF4, 0x51, 0x4B, 0x34, 0x1F, 0xB5, 0x44, 0x29, 0x94, 0xAA, 0x61, 0x47, 0x7E,
0x70, 0xA3, 0x4C, 0x42, 0x2F, 0x4D, 0x47, 0x2A, 0x99, 0x16, 0x15, 0x66, 0x63, 0xD5, 0x4C, 0x21,
0xC0, 0x23, 0x15, 0x21, 0x63, 0x5B, 0x02, 0x1F, 0x98, 0xE4, 0x65, 0x83, 0x45, 0x46, 0xA1, 0x2A,
0xB3, 0x84, 0x4D, 0x47, 0xEA, 0x8C, 0xF3, 0x45, 0x3A, 0x68, 0xB5, 0xAE, 0x02, 0x3E, 0x5B, 0x8E,
0x9B, 0x93, 0x78, 0xDE, 0x1A, 0xFB, 0x09, 0xF0, 0xAD, 0xF5, 0x5B, 0x32, 0x0E, 0x2F, 0x4F, 0xD2,
0x85, 0x63, 0xAB, 0x0A, 0x07, 0x29, 0x66, 0x7C, 0xA4, 0x5E, 0x42, 0x78, 0x1B, 0x7D, 0x01, 0xA8,
0xE9, 0xCD, 0x55, 0xBE, 0x0E, 0x9B, 0x03, 0x30, 0x62, 0xAD, 0x7C, 0xB8, 0x09, 0xD8, 0xEA, 0x97,
0xF8, 0x76, 0xA4, 0x62, 0x08, 0x6D, 0x39, 0x26, 0x7C, 0xD9, 0xA6, 0x09, 0xB3, 0xAE, 0x84, 0x57,
0xC1, 0xAC, 0x7C, 0xA4, 0xD2, 0x2D, 0x68, 0x09, 0xAB, 0xB7, 0x4D, 0x1D, 0x07, 0x68, 0x40, 0x3E,
0x2F, 0x64, 0x75, 0x4B, 0x57, 0x0C, 0x4B, 0x83, 0xE1, 0x0B, 0x8F, 0xCF, 0x14, 0x7F, 0xA4, 0xBE,
0xED, 0x20, 0x08, 0xAB, 0xEB, 0x5E, 0x3B, 0x0E, 0x40, 0xEC, 0xBA, 0x8A, 0xD1, 0x0E, 0x9D, 0x1E,
0x8C, 0x6A, 0xDB, 0x61, 0x1B, 0x2E, 0xD7, 0x6E, 0x1F, 0xBE, 0x5D, 0xA5, 0x0F, 0x3D, 0x4E, 0x1F,
0x9B, 0xEC, 0xD0, 0x72, 0x5C, 0xA5, 0x67, 0x5E, 0x77, 0x2C, 0xC5, 0x70, 0x7B, 0x8A, 0x65, 0x42,
0x97, 0x65, 0xB6, 0x43, 0xA3, 0x67, 0xC2, 0x8D, 0xE3, 0x86, 0x0E, 0x00, 0xB9, 0xB6, 0x61, 0xA8,
0xEB, 0x2A, 0x0E, 0x4C, 0xEF, 0x3B, 0x21, 0x0C, 0xED, 0x84, 0x00, 0x13, 0x80, 0xF4, 0xAE, 0xB1,
0xC7, 0x51, 0xE0, 0xBB, 0xEB, 0x5C, 0xC3, 0x14, 0x07, 0x17, 0x85, 0x07, 0x37, 0x34, 0xE4, 0x08,
0xB8, 0x81, 0xF1, 0xD7, 0xF0, 0x08, 0x23, 0xFB, 0xB8, 0x30, 0x01, 0x31, 0x10, 0x70, 0x28, 0x57,
0xB9, 0xC6, 0xB5, 0x0D, 0xC4, 0xA1, 0x40, 0x80, 0x10, 0xB3, 0x42, 0x84, 0xE6, 0x5C, 0xE3, 0xEA,
0x06, 0x62, 0x21, 0x51, 0x37, 0x08, 0x77, 0x43, 0x6C, 0xCE, 0x52, 0xAE, 0x11, 0x07, 0xB1, 0x2E,
0xA2, 0x6B, 0xD0, 0xFE, 0xF1, 0xA1, 0x4D, 0x63, 0x60, 0x08, 0xCE, 0xB0, 0xAF, 0x11, 0x01, 0xD8,
0x3F, 0x42, 0x11, 0x40, 0x1C, 0xB1, 0x8E, 0xD1, 0xB3, 0xAE, 0x8D, 0x8E, 0xA9, 0x20, 0x16, 0x88,
0x01, 0x22, 0xD0, 0x43, 0x9E, 0xB8, 0x88, 0x27, 0x00, 0x84, 0xA5, 0x5D, 0x44, 0xA4, 0xA7, 0x20,
0xEA, 0xB6, 0xD2, 0x09, 0x69, 0x5D, 0xD8, 0xBF, 0xD1, 0x51, 0x5C, 0xD8, 0x67, 0x07, 0xC8, 0x0D,
0xFB, 0x87, 0x85, 0xE1, 0x0E, 0x48, 0x44, 0x9D, 0x21, 0x0C, 0xBC, 0xB6, 0x1C, 0x04, 0x2B, 0x66,
0x3A, 0x8A, 0xA0, 0x2C, 0x6E, 0xD9, 0xED, 0x2A, 0xB0, 0x61, 0x58, 0x89, 0x56, 0xB3, 0x60, 0x26,
0xF4, 0x84, 0x88, 0x25, 0xAC, 0x04, 0xEB, 0x09, 0x1C, 0xA1, 0x37, 0xA4, 0x1D, 0x40, 0x33, 0x92,
0x19, 0xF7, 0xF4, 0x95, 0x18, 0xDD, 0x03, 0x82, 0x5E, 0x1B, 0xBD, 0x3E, 0xEE, 0x94, 0x48, 0xDD,
0x71, 0x38, 0x7C, 0x88, 0x20, 0xCD, 0x36, 0x2F, 0xEE, 0xB2, 0x4E, 0xBC, 0xC2, 0x05, 0x3A, 0x44,
0xBB, 0x51, 0xDC, 0x89, 0xAE, 0xAF, 0x20, 0x4B, 0x2D, 0x14, 0x26, 0xB8, 0x5C, 0xC1, 0x07, 0x84,
0xF7, 0x58, 0x39, 0x82, 0x70, 0x26, 0xCA, 0x75, 0x22, 0xCB, 0xDA, 0xD4, 0xE3, 0xD7, 0x41, 0x32,
0x5F, 0x41, 0xD8, 0x03, 0xC3, 0x60, 0x00, 0x8C, 0xF6, 0xE0, 0x83, 0x0A, 0xF4, 0x08, 0x25, 0x5A,
0xAD, 0x56, 0xCD, 0x92, 0x22, 0x85, 0xCB, 0x89, 0x21, 0x1E, 0x5B, 0xA4, 0xD1, 0xC6, 0xC7, 0x93,
0x5F, 0x3E, 0x9C, 0xB6, 0x38, 0xD8, 0x88, 0x96, 0xDD, 0xB4, 0xFE, 0x1A, 0x6A, 0x65, 0xF6, 0xDD,
0xEB, 0x9E, 0x8D, 0x10, 0x3B, 0x66, 0x13, 0xA5, 0xCF, 0x46, 0xD2, 0xBA, 0x40, 0xFC, 0x76, 0x9F,
0x5B, 0x56, 0x07, 0xDB, 0x7A, 0xD8, 0xD6, 0x77, 0xF1, 0xB6, 0x0F, 0x1C, 0xE8, 0xD1, 0xC5, 0xB5,
0xF3, 0x2E, 0x14, 0xBD, 0x76, 0x97, 0x08, 0x9E, 0xDF, 0xA1, 0xE0, 0x52, 0xA7, 0xD1, 0xE9, 0xC9,
0x89, 0x46, 0x0E, 0xC2, 0x28, 0x03, 0x36, 0xB2, 0xD5, 0x80, 0x5D, 0xFD, 0x1C, 0x05, 0xF9, 0x60,
0xE7, 0x23, 0x68, 0x00, 0x4D, 0x13, 0xB3, 0x08, 0x58, 0x3F, 0x83, 0xDF, 0x17, 0x4B, 0x66, 0x00,
0x15, 0x42, 0x22, 0xBB, 0x12, 0xAA, 0xD4, 0x05, 0xB8, 0xF7, 0xDB, 0x0A, 0xCF, 0xE6, 0x96, 0xE0,
0xC9, 0x25, 0x04, 0x15, 0x70, 0xD5, 0xAF, 0x6F, 0x7B, 0xBD, 0x1E, 0xF4, 0xF5, 0x49, 0xC5, 0x51,
0xCB, 0x2D, 0x90, 0x57, 0x9B, 0x13, 0x82, 0x64, 0x39, 0xDA, 0x5D, 0x94, 0x67, 0x40, 0xAA, 0x8F,
0x16, 0xC2, 0xB2, 0x51, 0xDF, 0x80, 0x36, 0x36, 0x0C, 0xC2, 0x2F, 0x7C, 0x12, 0x37, 0x78, 0x85,
0x1E, 0xB8, 0xBD, 0xC6, 0x45, 0x14, 0x1B, 0x04, 0xD4, 0x02, 0xB2, 0x2B, 0x56, 0x5F, 0x71, 0x69,
0x39, 0xC0, 0xB9, 0x8B, 0x5B, 0x87, 0x11, 0x46, 0x17, 0x80, 0x75, 0xD0, 0xA0, 0x75, 0x10, 0x6A,
0x0F, 0x8C, 0x88, 0x85, 0x32, 0xDF, 0x51, 0x84, 0xA9, 0x31, 0x91, 0x17, 0x70, 0x05, 0x14, 0xAF,
0x6D, 0xB4, 0x44, 0xA0, 0xA8, 0x5D, 0x30, 0x0A, 0x16, 0xC7, 0x89, 0x3D, 0x9B, 0xF7, 0x05, 0x63,
0x2C, 0xD8, 0x1D, 0x1A, 0x8F, 0x1E, 0x6E, 0xCE, 0x71, 0x88, 0xB0, 0xB8, 0x98, 0x7C, 0xB0, 0x5D,
0xEA, 0xA7, 0x6E, 0x9A, 0xD1, 0x43, 0x8D, 0xE9, 0x9A, 0xE2, 0x0A, 0x10, 0xBB, 0xB0, 0xD0, 0xB5,
0x05, 0x9A, 0x0C, 0x24, 0x53, 0xDC, 0x8C, 0xAE, 0x2E, 0xF4, 0x5E, 0x1B, 0x7D, 0xB2, 0xC7, 0x88,
0x14, 0xEC, 0xA4, 0xD7, 0xFF, 0xFA, 0xD6, 0x05, 0x53, 0xD0, 0xB5, 0xBB, 0x60, 0x55, 0xD0, 0x9A,
0x48, 0xAB, 0x48, 0x1F, 0xE2, 0xA8, 0x83, 0xAB, 0x10, 0xF3, 0x69, 0xBE, 0x03, 0x53, 0x91, 0x13,
0xB8, 0x2D, 0x0B, 0x2C, 0x09, 0x6E, 0xCD, 0x51, 0x1C, 0x92, 0x13, 0xCB, 0xE2, 0x0E, 0x32, 0xC5,
0xEA, 0x86, 0x00, 0x0B, 0xEC, 0x09, 0x2C, 0x8A, 0xF4, 0x47, 0x14, 0x11, 0x71, 0xC0, 0xA2, 0x23,
0x6F, 0xC9, 0x7A, 0xA2, 0x01, 0x05, 0x63, 0x01, 0xE8, 0xC0, 0xA2, 0x84, 0xAD, 0x61, 0x03, 0xA9,
0x4D, 0x6E, 0x38, 0x36, 0xD2, 0xF3, 0x49, 0xCA, 0x7F, 0x8A, 0x19, 0xD2, 0x14, 0xD2, 0x9C, 0xE7,
0x68, 0xFF, 0x21, 0x17, 0xDA, 0x5A, 0x05, 0x5F, 0x82, 0xBF, 0x86, 0xC2, 0x5B, 0xDD, 0xEE, 0x35,
0x32, 0xCF, 0x04, 0xB1, 0x03, 0xDA, 0xB9, 0x6D, 0x94, 0x8F, 0x9E, 0x2B, 0xA4, 0x0F, 0xAC, 0xA9,
0xED, 0x90, 0xD4, 0x21, 0xC3, 0xDA, 0x42, 0x1B, 0x5D, 0x6E, 0x94, 0x6E, 0x4B, 0x03, 0x8C, 0xD2,
0x3C, 0xA3, 0x80, 0x46, 0xB7, 0xE2, 0x4E, 0x0C, 0xA0, 0x7E, 0x9C, 0x27, 0xA7, 0x11, 0x34, 0x04,
0x96, 0xDF, 0x14, 0x9D, 0xC5, 0x8C, 0x0C, 0xCA, 0xD7, 0xB7, 0x6D, 0x50, 0x9E, 0xBE, 0x0B, 0xAE,
0xCC, 0x26, 0xAF, 0x00, 0x1A, 0x64, 0xB4, 0xA5, 0xA1, 0x37, 0x6C, 0xD4, 0x07, 0x90, 0x72, 0x29,
0x64, 0x24, 0x60, 0xC2, 0x67, 0x48, 0x13, 0x83, 0x02, 0x88, 0xFA, 0x09, 0xAA, 0x6A, 0xD3, 0x65,
0x66, 0x39, 0xD6, 0xB5, 0x83, 0x70, 0x14, 0x90, 0x30, 0xCB, 0xBA, 0xEE, 0x60, 0x87, 0x4D, 0x7A,
0xDF, 0x13, 0x28, 0xF5, 0xAE, 0x6D, 0xA4, 0xB9, 0x43, 0xB0, 0x2C, 0x5C, 0xC1, 0xA2, 0x5B, 0x1B,
0x96, 0x20, 0x58, 0xB0, 0x70, 0x17, 0x83, 0x02, 0xD8, 0x2B, 0x0A, 0x2F, 0xB8, 0x4B, 0x8B, 0xFC,
0x15, 0xA9, 0x19, 0x12, 0x89, 0xC4, 0x9E, 0x0C, 0x21, 0x2E, 0x4A, 0x20, 0x0C, 0xD4, 0x56, 0xAB,
0x8B, 0x44, 0x11, 0xCA, 0x88, 0x36, 0x8E, 0xB4, 0x03, 0xFA, 0x00, 0x7F, 0x17, 0xD5, 0x06, 0x10,
0x56, 0xA8, 0x11, 0xB1, 0xE7, 0x84, 0x94, 0x01, 0xC3, 0x67, 0x16, 0x84, 0x21, 0x82, 0x67, 0x4A,
0x8F, 0x3B, 0x84, 0xA9, 0x83, 0x16, 0xC3, 0xED, 0x71, 0x1B, 0xD7, 0xEA, 0x22, 0x0D, 0xC1, 0x9A,
0x9B, 0x80, 0x1E, 0x9A, 0x03, 0x60, 0x70, 0xCF, 0x51, 0x38, 0x99, 0x09, 0x30, 0x82, 0x40, 0x21,
0x1C, 0xE5, 0x10, 0xF9, 0x3B, 0xA0, 0x51, 0x3D, 0x6C, 0x41, 0xE3, 0x03, 0x4E, 0xB7, 0x0B, 0x60,
0x4C, 0x73, 0x06, 0xD8, 0x98, 0x80, 0x81, 0x49, 0x1B, 0xE9, 0xE6, 0xF8, 0x0B, 0xBB, 0x04, 0xDF,
0x37, 0x34, 0x80, 0x76, 0xA3, 0xE4, 0x8D, 0x3C, 0x1F, 0x39, 0xC3, 0x5E, 0x9A, 0x4D, 0x4D, 0xD8,
0xD7, 0x25, 0x3D, 0x86, 0x99, 0x62, 0xA2, 0x65, 0xD2, 0x40, 0x6A, 0x92, 0x06, 0x0F, 0x3E, 0x4F,
0x52, 0xD0, 0xDF, 0x59, 0xB8, 0xD8, 0xA1, 0x9B, 0x0A, 0x85, 0xCF, 0x23, 0xB5, 0x88, 0xA9, 0xD5,
0xA2, 0x2F, 0x8E, 0x26, 0x61, 0x30, 0xF9, 0x32, 0x52, 0xCF, 0xDE, 0xD4, 0xB5, 0xA1, 0xAA, 0x04,
0xA0, 0x0A, 0x61, 0x0C, 0x59, 0x59, 0x00, 0xA1, 0xB8, 0xBA, 0xAD, 0xD6, 0x55, 0xAD, 0x6C, 0x3A,
0x15, 0xBD, 0x6C, 0xDA, 0xDF, 0x5D, 0x33, 0xA7, 0x41, 0x18, 0xCA, 0x4D, 0xAA, 0xA4, 0xA6, 0x7D,
0x8C, 0x82, 0x4C, 0xF3, 0xC6, 0x26, 0x6E, 0xF6, 0xA4, 0x11, 0x87, 0x00, 0x4F, 0xC4, 0x34, 0x24,
0xE1, 0xD8, 0x32, 0x33, 0x60, 0x65, 0x08, 0x95, 0x6C, 0xE2, 0x98, 0x6B, 0x09, 0x27, 0xDA, 0xA6,
0x58, 0xD8, 0xBA, 0x01, 0xC1, 0x43, 0x9E, 0xE2, 0x08, 0x97, 0x84, 0xB3, 0x2B, 0xFC, 0x7B, 0x9F,
0x44, 0x51, 0xC8, 0xA7, 0x89, 0x5C, 0xED, 0xD0, 0x32, 0x38, 0xA8, 0x68, 0xE5, 0xC5, 0xE0, 0x19,
0xA0, 0x73, 0x4D, 0x10, 0xA8, 0x8D, 0xE6, 0x0B, 0x91, 0xC3, 0xD9, 0x62, 0x32, 0xAE, 0x9D, 0xB7,
0x71, 0x23, 0x1F, 0x48, 0xEB, 0x83, 0x77, 0x90, 0x1B, 0x12, 0x62, 0x60, 0x5B, 0x68, 0xD0, 0x1D,
0xD4, 0x60, 0xD4, 0x3F, 0x90, 0xC1, 0x19, 0xA0, 0xAA, 0x48, 0x95, 0x43, 0xB9, 0x22, 0x3B, 0x40,
0x8A, 0x21, 0xA5, 0x8E, 0x76, 0xFA, 0x55, 0x6D, 0x95, 0x44, 0x64, 0x17, 0xFB, 0x4B, 0x95, 0x34,
0xCA, 0xF6, 0x90, 0x95, 0x28, 0x47, 0xC8, 0xF1, 0xD7, 0x1F, 0xFF, 0xF3, 0xE4, 0xEC, 0xFC, 0xF4,
0xDD, 0x1F, 0xEA, 0x0E, 0xB1, 0xCA, 0x45, 0x0A, 0xE1, 0xB5, 0x30, 0x65, 0x6A, 0x89, 0x43, 0x0E,
0x47, 0x2D, 0x48, 0xB3, 0x76, 0xE6, 0x5A, 0xA2, 0x54, 0x77, 0x7C, 0x34, 0xB3, 0x09, 0xFC, 0xDB,
0xF3, 0xDF, 0x10, 0xCC, 0xCC, 0x86, 0xAF, 0xAC, 0x6B, 0xF7, 0x5C, 0x45, 0x66, 0x9D, 0x42, 0x10,
0x5F, 0x9F, 0xBE, 0x39, 0x39, 0xFF, 0xDB, 0xF9, 0xC5, 0xC9, 0x5B, 0x75, 0x7B, 0x68, 0xF6, 0x66,
0x1D, 0xA2, 0x52, 0x68, 0x9D, 0x29, 0xAF, 0x83, 0x90, 0xA5, 0x77, 0x29, 0x67, 0xF3, 0x3D, 0xB0,
0x29, 0x33, 0x07, 0x40, 0x54, 0xBA, 0x54, 0xA8, 0x74, 0xA9, 0x62, 0xB1, 0x52, 0xAC, 0x45, 0x65,
0x4B, 0x51, 0x3F, 0x53, 0x95, 0xC8, 0x9B, 0x43, 0xE7, 0xFC, 0x0E, 0x1B, 0xD3, 0x4F, 0x9F, 0x55,
0x65, 0xBE, 0x0C, 0x79, 0xB0, 0x40, 0x32, 0x66, 0x77, 0x2A, 0xE8, 0xA1, 0x80, 0x54, 0x28, 0x88,
0x52, 0x7A, 0x31, 0xA6, 0xCA, 0x15, 0x44, 0x09, 0x54, 0xAC, 0x51, 0xA9, 0x8A, 0xAA, 0x85, 0xEE,
0x9D, 0xB3, 0xC8, 0xC7, 0xA5, 0x48, 0x03, 0x6F, 0xBC, 0x70, 0x09, 0xF3, 0x3E, 0xD0, 0x58, 0xF5,
0xF8, 0x45, 0x34, 0x4E, 0x17, 0x43, 0xF1, 0x7D, 0xB4, 0x48, 0xE2, 0xAB, 0x84, 0xA5, 0x69, 0xC6,
0xD3, 0x9B, 0x20, 0x0D, 0xC6, 0x41, 0x18, 0xF0, 0xBB, 0x01, 0x10, 0xCE, 0x67, 0x51, 0x86, 0xFA,
0x22, 0xB9, 0x12, 0x4B, 0xD2, 0x0D, 0x64, 0xDB, 0x94, 0xF2, 0x92, 0x31, 0x91, 0x20, 0x20, 0x53,
0x4E, 0xC4, 0x67, 0x07, 0xFF, 0xF6, 0x91, 0x4E, 0xF2, 0x5D, 0xA4, 0xCD, 0x99, 0x15, 0x20, 0x7B,
0xF2, 0x14, 0x52, 0x54, 0xF6, 0xFD, 0x6B, 0x3C, 0x9F, 0x7B, 0x91, 0x5F, 0xAF, 0x85, 0x41, 0xCA,
0x6B, 0x7A, 0xCD, 0x0B, 0xC3, 0x5A, 0x89, 0x0C, 0x67, 0x6C, 0x0A, 0xD8, 0xCE, 0x4A, 0x16, 0xAB,
0xBC, 0x2A, 0xE2, 0x99, 0x43, 0xFB, 0x35, 0x61, 0x60, 0x4E, 0xFC, 0x20, 0xA9, 0x6B, 0xEA, 0x21,
0xAB, 0xE5, 0x9A, 0x85, 0xC9, 0xC2, 0xFB, 0x8A, 0xBD, 0x72, 0xF1, 0x7F, 0x18, 0x9F, 0x80, 0x1C,
0x28, 0xD0, 0xD6, 0x56, 0x95, 0x3B, 0xA4, 0x9D, 0x9A, 0xCD, 0x76, 0x4A, 0xB3, 0x6D, 0xB8, 0x4F,
0x60, 0x90, 0x0D, 0x97, 0x3B, 0xBA, 0x08, 0x73, 0x25, 0xCB, 0xAB, 0xA8, 0x92, 0x19, 0x1C, 0x1C,
0x7A, 0x47, 0xE0, 0x32, 0xDB, 0xD9, 0x2E, 0x19, 0xCE, 0xF6, 0x83, 0x70, 0x50, 0x7B, 0x11, 0x8E,
0x25, 0x10, 0xB2, 0xE1, 0x92, 0x17, 0x95, 0xA1, 0xB5, 0x27, 0x1F, 0x57, 0x12, 0x22, 0x18, 0x94,
0x0C, 0x08, 0xD5, 0xA7, 0xD5, 0xE3, 0x06, 0x10, 0x10, 0x60, 0xE4, 0x06, 0x82, 0x54, 0x64, 0x83,
0xA6, 0xD2, 0x37, 0x20, 0x55, 0x49, 0x76, 0xC0, 0x00, 0xE7, 0x94, 0x0C, 0xA2, 0x69, 0x9C, 0x49,
0x63, 0x79, 0x76, 0xC5, 0x20, 0x88, 0x1A, 0x8B, 0x9C, 0x21, 0x1E, 0x2A, 0x87, 0x95, 0xD4, 0x4C,
0x70, 0x8B, 0x8A, 0x3A, 0x4A, 0x95, 0xA8, 0xDC, 0x90, 0x54, 0xCD, 0xCA, 0xFC, 0xBD, 0x00, 0xB1,
0x01, 0xD8, 0x33, 0x6C, 0x3F, 0xFE, 0x03, 0x04, 0x3B, 0x7F, 0x38, 0x87, 0x6D, 0x67, 0x0F, 0xC2,
0x54, 0x9C, 0x5F, 0xF2, 0x60, 0x0E, 0xFB, 0xBC, 0x08, 0x8A, 0x61, 0x15, 0x59, 0xD9, 0x68, 0xCB,
0xFD, 0xE0, 0x2C, 0xDF, 0x83, 0x44, 0x03, 0x65, 0x3D, 0x37, 0x09, 0x97, 0x28, 0x96, 0x34, 0x4E,
0x14, 0x96, 0x0E, 0x9B, 0x3E, 0x59, 0xDE, 0x13, 0xAA, 0x97, 0x72, 0x8F, 0x2F, 0x53, 0x35, 0xA7,
0xF5, 0xD6, 0xF7, 0x03, 0xC6, 0xEF, 0xE3, 0x87, 0xF7, 0xAF, 0x7E, 0xBE, 0x38, 0x39, 0x6C, 0xFA,
0x64, 0x42, 0xAE, 0x7C, 0x58, 0xF8, 0x20, 0xFC, 0x0F, 0x58, 0xBE, 0x8A, 0xFA, 0xEE, 0x35, 0x84,
0xAB, 0xBD, 0x66, 0xB0, 0x14, 0xEE, 0x3F, 0xD9, 0xF4, 0xC1, 0x43, 0x49, 0xF3, 0x85, 0x75, 0xDB,
0xB6, 0x79, 0xB8, 0x89, 0xF2, 0x32, 0x4F, 0x31, 0x78, 0xD3, 0x55, 0x6E, 0xF2, 0xF0, 0x76, 0xB7,
0xD1, 0xCB, 0x21, 0xE7, 0xBE, 0x6F, 0x9E, 0x5E, 0xA9, 0xFB, 0xC1, 0x1F, 0x9F, 0x31, 0xE0, 0x63,
0xC2, 0x81, 0xDA, 0xBA, 0x02, 0x66, 0xDF, 0x4B, 0x99, 0xB2, 0xF2, 0x02, 0xDE, 0x84, 0xFF, 0x32,
0xC7, 0x98, 0x83, 0x9A, 0xC4, 0x4B, 0x74, 0x6E, 0x0F, 0xBB, 0xCC, 0x82, 0x4D, 0x79, 0xBC, 0x85,
0xB5, 0xCC, 0x5C, 0xD9, 0xA8, 0x18, 0x5A, 0x65, 0x7C, 0xA5, 0x3E, 0xBA, 0xAB, 0x4B, 0x94, 0xC5,
0xA1, 0x67, 0xE6, 0x1C, 0x9F, 0x02, 0xEA, 0x3C, 0x98, 0x06, 0x13, 0x7A, 0xC9, 0x05, 0x9E, 0xD7,
0xD9, 0x21, 0x73, 0x45, 0xB9, 0x5A, 0x86, 0x02, 0xC7, 0x95, 0xC0, 0x52, 0x74, 0xA3, 0xCD, 0x50,
0x95, 0x3C, 0x58, 0x3B, 0xFE, 0x90, 0x82, 0xDA, 0xCA, 0xED, 0x6D, 0x38, 0xC0, 0xF2, 0x99, 0xA6,
0x4C, 0x04, 0xC4, 0x74, 0xDA, 0x24, 0xF2, 0xBF, 0x12, 0x89, 0x62, 0xD9, 0xBF, 0x20, 0xD5, 0x2C,
0x79, 0x3C, 0x12, 0xEF, 0xA1, 0x6F, 0x05, 0x16, 0xE4, 0x09, 0x88, 0x2C, 0xE4, 0x14, 0x89, 0xCC,
0xE2, 0x30, 0x32, 0xE3, 0x5D, 0x01, 0x4A, 0xB9, 0x80, 0xBF, 0x11, 0x46, 0x64, 0x92, 0xBE, 0x4F,
0x1F, 0x0A, 0x8F, 0xF7, 0xE6, 0x0C, 0xBD, 0x93, 0x94, 0xF7, 0xF3, 0xE5, 0x78, 0x1E, 0xF0, 0x9D,
0x16, 0x22, 0x9D, 0x80, 0xC1, 0xE4, 0xC7, 0x37, 0x5E, 0xA2, 0xAC, 0xD2, 0xCB, 0x34, 0x5E, 0x26,
0x13, 0xA6, 0xDF, 0xCE, 0x43, 0xCC, 0xA7, 0x45, 0x18, 0xA1, 0x4F, 0x96, 0x09, 0xBE, 0x80, 0x44,
0x2B, 0x3D, 0x52, 0x5B, 0xAA, 0x0E, 0x5B, 0x98, 0x21, 0xDB, 0x05, 0xD3, 0x47, 0x3F, 0x58, 0xFA,
0x8A, 0x8D, 0xD3, 0x78, 0xF2, 0x85, 0xF1, 0xCB, 0x45, 0x9C, 0xF0, 0x91, 0x59, 0x6A, 0x38, 0x7D,
0x3F, 0x52, 0x61, 0x4A, 0x7A, 0x17, 0x4D, 0x2E, 0xA1, 0x15, 0xF2, 0xF2, 0xF9, 0x32, 0x2A, 0x4D,
0x45, 0x71, 0xBC, 0x44, 0x52, 0xA9, 0x3A, 0x88, 0xE7, 0x65, 0x3C, 0x9D, 0x56, 0x01, 0x92, 0x52,
0x30, 0x1F, 0x1B, 0x59, 0xBA, 0xB8, 0x64, 0x49, 0x12, 0x27, 0x97, 0x73, 0x50, 0x31, 0x98, 0x87,
0x93, 0x8A, 0xC6, 0x49, 0xEC, 0x33, 0x58, 0x1A, 0x09, 0x25, 0x10, 0x1F, 0x99, 0xC3, 0xE9, 0x32,
0xA2, 0x77, 0xB1, 0xA0, 0xBC, 0x37, 0x63, 0x0F, 0x1C, 0xF6, 0x3D, 0x6E, 0x14, 0x26, 0x96, 0x4C,
0x94, 0xAA, 0xF3, 0x51, 0x69, 0x8B, 0xCD, 0x74, 0x01, 0x7A, 0x59, 0x87, 0x8D, 0x6A, 0x7A, 0x44,
0xFB, 0x0D, 0x46, 0xD6, 0x10, 0x58, 0x5D, 0x67, 0x0D, 0x9C, 0xE7, 0x4B, 0xE2, 0xD7, 0x84, 0xCB,
0xAF, 0x29, 0x39, 0xD1, 0xFF, 0x54, 0xCB, 0x94, 0xAA, 0xB5, 0x6A, 0x43, 0x65, 0x7F, 0xE0, 0xF1,
0xA7, 0x7A, 0xDC, 0x22, 0xB5, 0x55, 0x87, 0xC1, 0x11, 0x6F, 0x86, 0x2C, 0xBA, 0xE2, 0x10, 0xA8,
0x0F, 0xB5, 0x3D, 0xAB, 0xEC, 0x59, 0x44, 0x6D, 0xD4, 0xA3, 0xC6, 0x88, 0x7F, 0x0A, 0x3E, 0x37,
0x10, 0xE3, 0x86, 0xFA, 0xD0, 0xA2, 0x6A, 0x43, 0x0C, 0xCE, 0x0D, 0x94, 0xC4, 0x42, 0x0F, 0x1A,
0x8D, 0x61, 0xC2, 0xF8, 0x32, 0x89, 0x14, 0x42, 0xA1, 0x6C, 0x4D, 0xD4, 0x75, 0x4E, 0x48, 0x50,
0x8E, 0x74, 0x76, 0x89, 0x49, 0x1B, 0x10, 0x53, 0x8C, 0x57, 0xB3, 0xE0, 0xA3, 0xD6, 0xB6, 0x6B,
0x10, 0x34, 0xD4, 0x2C, 0xB8, 0x40, 0x98, 0x51, 0xEB, 0xD4, 0x30, 0xCC, 0xC0, 0x8B, 0xF0, 0x85,
0x35, 0xBB, 0x5D, 0xCB, 0x62, 0x91, 0x5A, 0xB7, 0x26, 0x15, 0xA3, 0x86, 0xE1, 0xC3, 0x20, 0x61,
0xFE, 0xB0, 0xA6, 0xB4, 0x00, 0x91, 0x6D, 0x70, 0xBB, 0x01, 0xD8, 0x55, 0x00, 0x14, 0x7E, 0x6C,
0x81, 0x70, 0x4C, 0x01, 0xA2, 0xB7, 0x07, 0xA3, 0x4E, 0xB7, 0x00, 0x08, 0x36, 0xFC, 0x61, 0x9C,
0xEC, 0x2A, 0x40, 0xCB, 0x14, 0x10, 0xF1, 0x2A, 0x41, 0xF6, 0xCA, 0x20, 0xDD, 0x47, 0x43, 0xB4,
0xFB, 0x3B, 0x21, 0x38, 0x8F, 0xD9, 0xA5, 0x2B, 0x40, 0xB8, 0x8E, 0x40, 0xAA, 0x2B, 0x70, 0xEA,
0xE6, 0x00, 0x4B, 0xF0, 0x3A, 0x8F, 0x02, 0xD8, 0xF9, 0xDE, 0x00, 0x7B, 0xDF, 0x03, 0xA0, 0x08,
0x29, 0x11, 0x6C, 0x11, 0x65, 0xD7, 0x6C, 0xB7, 0x24, 0x12, 0x70, 0x9F, 0x45, 0xD9, 0x35, 0xAA,
0x0A, 0xD8, 0x58, 0x2D, 0xEE, 0xD5, 0x8E, 0xBF, 0xA7, 0x88, 0xFE, 0xBD, 0xF2, 0xF9, 0x7D, 0x85,
0xF3, 0x3B, 0x4B, 0xE6, 0xDF, 0x2B, 0x96, 0xDF, 0x57, 0x26, 0xBF, 0xAF, 0x40, 0xFE, 0x43, 0xA4,
0xB1, 0x30, 0x8D, 0xF8, 0xFA, 0x7C, 0xD3, 0x32, 0x3E, 0x56, 0x50, 0x6D, 0x17, 0xFE, 0xAF, 0xE5,
0x55, 0xE2, 0xDA, 0xDB, 0xAE, 0xEE, 0x28, 0x6F, 0x6C, 0xBD, 0xA7, 0xBC, 0xE9, 0xEA, 0x96, 0x43,
0xDF, 0xA6, 0xF2, 0xC6, 0x92, 0x97, 0x9E, 0x6E, 0x59, 0xE2, 0xD2, 0x16, 0x8D, 0x1D, 0xB8, 0x98,
0x74, 0xE9, 0xEB, 0x56, 0x97, 0xBE, 0xFB, 0xD4, 0x64, 0xC3, 0x70, 0x5B, 0x5E, 0x6C, 0xDD, 0xEA,
0xD1, 0xA5, 0x47, 0x6D, 0x1D, 0x84, 0xDA, 0x51, 0xBE, 0xE2, 0x06, 0x93, 0xF8, 0x0B, 0xEC, 0x90,
0x8A, 0x31, 0x35, 0x91, 0xCF, 0xD5, 0x68, 0xA7, 0x3B, 0x37, 0x2A, 0xC2, 0xF6, 0x4B, 0xCC, 0x80,
0x99, 0x76, 0x5F, 0xF2, 0x47, 0x8D, 0x11, 0x43, 0x37, 0xA4, 0x97, 0x3D, 0x90, 0x4A, 0x89, 0x8D,
0xAE, 0x82, 0x07, 0x52, 0xB5, 0x02, 0x06, 0x04, 0x02, 0x78, 0xAE, 0xE9, 0x9C, 0x63, 0x8D, 0x3E,
0xAD, 0x33, 0x9D, 0x67, 0x44, 0xAB, 0xB3, 0x11, 0x6B, 0xF2, 0xF8, 0x4D, 0xBC, 0x62, 0xC9, 0xAF,
0x10, 0x06, 0xD7, 0x35, 0xED, 0xA8, 0xCE, 0x47, 0x7C, 0xA3, 0xED, 0xA5, 0x61, 0x0D, 0xF8, 0x11,
0x7B, 0x69, 0x0D, 0xCC, 0x02, 0x2A, 0x9E, 0x21, 0xF0, 0xF8, 0x64, 0x46, 0x89, 0x04, 0xA5, 0x45,
0x88, 0x21, 0xFA, 0x7C, 0x8E, 0xC1, 0x02, 0x46, 0x1C, 0xC3, 0x60, 0x0A, 0xD0, 0xD4, 0x72, 0xB9,
0xE3, 0x9C, 0x46, 0x0E, 0x14, 0xB5, 0xC1, 0x9A, 0x62, 0x96, 0xCE, 0x1B, 0xD5, 0x21, 0xDF, 0xCA,
0x0F, 0x17, 0x31, 0xF7, 0x42, 0x45, 0x9C, 0xC4, 0xA2, 0x49, 0x1C, 0x1B, 0x0E, 0xCF, 0x81, 0x78,
0xD6, 0x2F, 0x4F, 0x59, 0xC2, 0xF3, 0xE1, 0x19, 0xEF, 0x26, 0x93, 0xE5, 0x42, 0xFC, 0x06, 0x46,
0x51, 0x69, 0xE8, 0xD1, 0x9C, 0x41, 0x48, 0xA8, 0xCC, 0x83, 0x08, 0x84, 0xA6, 0x46, 0x89, 0x87,
0xB0, 0x0B, 0x33, 0x90, 0xAA, 0x51, 0xAD, 0x0F, 0x77, 0x22, 0xE0, 0xAB, 0xE1, 0x0A, 0x71, 0x3E,
0x1F, 0xA2, 0x02, 0x60, 0x22, 0x4D, 0x96, 0x89, 0xF5, 0x66, 0xFF, 0x4F, 0xAA, 0xEE, 0xC7, 0x93,
0xE5, 0x1C, 0xF8, 0xD8, 0xBC, 0x62, 0xFC, 0x24, 0x64, 0x78, 0xFB, 0xCB, 0xDD, 0x29, 0xF0, 0x4F,
0xA6, 0x97, 0x5A, 0x33, 0x88, 0x22, 0x96, 0xFC, 0x7E, 0xF1, 0xF6, 0xCD, 0x88, 0xEB, 0x44, 0x4E,
0x60, 0xF5, 0x0F, 0xE5, 0xD0, 0x49, 0x50, 0x3A, 0xA8, 0x44, 0x53, 0x10, 0xC5, 0xF0, 0x53, 0x3C,
0xDD, 0xF4, 0x6E, 0x8A, 0x31, 0x95, 0x5E, 0xE9, 0x13, 0x41, 0x8F, 0xAD, 0x0D, 0x69, 0x77, 0x3C,
0xC9, 0x34, 0xAD, 0x7C, 0x5A, 0xF9, 0x40, 0xE8, 0x53, 0x89, 0xDA, 0x60, 0x08, 0xAB, 0x9B, 0x10,
0xC6, 0x58, 0x8F, 0x88, 0x82, 0x30, 0xC0, 0x82, 0x50, 0xA8, 0xA4, 0xB1, 0x45, 0x44, 0x04, 0xA2,
0x19, 0x62, 0x74, 0x0E, 0xF6, 0xA1, 0x76, 0x0C, 0xF9, 0x2E, 0xE6, 0x5F, 0x59, 0x7A, 0xA5, 0xAE,
0x59, 0x93, 0x64, 0xAB, 0x09, 0xF8, 0xF1, 0x7A, 0x26, 0x77, 0x65, 0xF1, 0xDD, 0x92, 0xEC, 0x26,
0x26, 0x8E, 0x3A, 0xA7, 0x8B, 0xB6, 0xD6, 0x90, 0x6A, 0xA3, 0x32, 0x89, 0x5E, 0xBC, 0xA8, 0x83,
0x5C, 0x9A, 0x1A, 0xC5, 0x98, 0x48, 0xC0, 0x10, 0x83, 0xDD, 0x18, 0xA2, 0xD7, 0xF8, 0x28, 0x5B,
0x4D, 0x50, 0x6A, 0x18, 0x37, 0x1A, 0x9A, 0x6A, 0x58, 0x40, 0x75, 0x01, 0xBD, 0x2E, 0xFB, 0x3F,
0xC5, 0x9F, 0x9B, 0x58, 0xA0, 0xD1, 0x00, 0x16, 0x91, 0xF2, 0xE2, 0xEC, 0x58, 0x8A, 0x0C, 0xA5,
0xA0, 0x60, 0x84, 0xCA, 0x96, 0xA7, 0x64, 0x90, 0x76, 0x18, 0x21, 0xE5, 0x58, 0x81, 0xFF, 0x0A,
0x4B, 0x64, 0xE9, 0x36, 0x58, 0x12, 0xDD, 0xB6, 0xD0, 0x1E, 0xD9, 0x78, 0xDF, 0x11, 0x97, 0x2E,
0xB5, 0x59, 0x68, 0x43, 0xDE, 0x58, 0xB6, 0xFC, 0xB6, 0x14, 0x1C, 0x66, 0x3D, 0xC2, 0xAA, 0xE0,
0xE1, 0x44, 0xE5, 0xD6, 0x12, 0xBE, 0xF8, 0x0E, 0xAF, 0x35, 0xE5, 0xD6, 0x86, 0x0B, 0x58, 0xDF,
0x3B, 0x9B, 0xFC, 0xE0, 0x06, 0x04, 0xF1, 0x68, 0x48, 0xF4, 0xAD, 0x5A, 0x2B, 0xDB, 0xA4, 0x2C,
0x35, 0x51, 0x1C, 0x0B, 0x0D, 0xB5, 0xA3, 0x8B, 0x57, 0x32, 0x7E, 0xFE, 0x53, 0x06, 0xD0, 0x7F,
0x66, 0x56, 0x5C, 0xCD, 0xCF, 0x7F, 0x2D, 0x6E, 0x87, 0xF4, 0xDE, 0x41, 0xBC, 0x3F, 0xAC, 0x81,
0x46, 0x90, 0x09, 0x2B, 0x91, 0x14, 0x39, 0xD6, 0xA8, 0xE5, 0x2F, 0x0C, 0xC5, 0xFB, 0xC2, 0x4A,
0xAA, 0x56, 0xAA, 0x5E, 0xD7, 0x70, 0xE5, 0x8D, 0xC9, 0xBA, 0x4A, 0x07, 0xFB, 0x9A, 0xF8, 0x83,
0xC9, 0xE6, 0xD5, 0x57, 0xE0, 0xDB, 0xC6, 0x80, 0x17, 0x2F, 0x4A, 0x23, 0xB6, 0xBB, 0xBF, 0x7D,
0x43, 0xD1, 0xB0, 0x34, 0xB9, 0x4B, 0x91, 0xB6, 0x61, 0xC5, 0xFD, 0xE2, 0xD5, 0x31, 0xEC, 0x51,
0xEC, 0x76, 0x43, 0x06, 0xE4, 0x58, 0xEA, 0x2D, 0x75, 0xCD, 0xBC, 0xF4, 0xDD, 0x2A, 0x7A, 0x9F,
0xC4, 0x0B, 0x96, 0xF0, 0xBB, 0xBA, 0x4A, 0x45, 0x2C, 0xED, 0x65, 0x1D, 0x84, 0xCD, 0x14, 0x53,
0x76, 0xC0, 0xC3, 0x41, 0x25, 0x78, 0xDA, 0x20, 0x1B, 0x28, 0xE1, 0xCB, 0xC7, 0x4C, 0xA0, 0xCC,
0x9F, 0x6A, 0x65, 0xEA, 0xFC, 0x29, 0xAB, 0xA2, 0x7F, 0xAA, 0x25, 0x15, 0x7E, 0x05, 0xBE, 0x83,
0xB3, 0x3A, 0x59, 0xA8, 0x2A, 0xA1, 0xD5, 0x9A, 0x86, 0xF9, 0x09, 0x02, 0x2D, 0x27, 0x19, 0x95,
0xBD, 0xE3, 0xD2, 0x42, 0x17, 0xF1, 0x0B, 0xA5, 0xBC, 0x50, 0x9B, 0x04, 0x34, 0x26, 0xD9, 0xD4,
0x98, 0x44, 0x6A, 0xCC, 0x68, 0x53, 0x63, 0x92, 0x6D, 0x8D, 0x79, 0x96, 0xAE, 0x94, 0xF4, 0xA4,
0x2F, 0x9C, 0x72, 0x1F, 0xDD, 0x2B, 0xB8, 0x66, 0xF0, 0xC2, 0xF2, 0xAB, 0x8D, 0x8E, 0xD6, 0x45,
0xBD, 0x70, 0x51, 0x93, 0xDA, 0xA4, 0x4E, 0x36, 0x0D, 0xC5, 0x0B, 0xBA, 0x67, 0x54, 0x2E, 0x87,
0xE6, 0xB7, 0xE9, 0xDB, 0x16, 0xBA, 0x05, 0xFD, 0x8F, 0xF3, 0xCE, 0x85, 0xF0, 0x13, 0x43, 0xAA,
0xD9, 0xA3, 0x92, 0x8B, 0x69, 0x1E, 0xCB, 0x6C, 0x1F, 0x7D, 0x2D, 0xDB, 0xD9, 0x92, 0x83, 0x2F,
0x31, 0x2A, 0x29, 0x18, 0x35, 0xCC, 0x38, 0xB5, 0xD1, 0x57, 0x08, 0x4B, 0x2E, 0x25, 0x25, 0x71,
0x29, 0x0D, 0xDE, 0x2D, 0x8E, 0x7A, 0x59, 0x1A, 0x9F, 0x2E, 0x55, 0xFB, 0xF1, 0x7D, 0x96, 0x60,
0xED, 0xF5, 0x88, 0x59, 0x0D, 0xB8, 0xEC, 0x12, 0xC3, 0x97, 0xAA, 0x3A, 0x50, 0xB1, 0x28, 0x7C,
0xC0, 0x95, 0xE2, 0xFB, 0xA9, 0xF2, 0xA4, 0xE8, 0xA5, 0x8A, 0x6F, 0x93, 0x94, 0x8A, 0x95, 0x50,
0x82, 0x14, 0x5C, 0x7C, 0x9A, 0x96, 0x2B, 0x81, 0xA2, 0xA0, 0xA1, 0x04, 0x1C, 0xD6, 0xC8, 0x4C,
0x56, 0xAD, 0x55, 0x93, 0x24, 0x51, 0x6A, 0x1B, 0x85, 0xA0, 0xDA, 0xF1, 0x6F, 0xB1, 0xC2, 0x63,
0x45, 0x1C, 0x61, 0x0C, 0x8A, 0xF3, 0x14, 0xDE, 0xF1, 0x01, 0xEC, 0x8A, 0x32, 0x74, 0xD5, 0xD7,
0xEF, 0x9D, 0x40, 0x75, 0xFB, 0xCA, 0x7E, 0x64, 0x95, 0xA5, 0x08, 0xC8, 0xA4, 0xBA, 0x63, 0x98,
0x18, 0x47, 0xD3, 0x20, 0x99, 0xD7, 0xD5, 0x5F, 0xC5, 0x8D, 0xE2, 0x63, 0x17, 0x8E, 0x89, 0xA7,
0x28, 0xD3, 0x22, 0x2E, 0x02, 0x7D, 0xAC, 0x04, 0x8F, 0x34, 0x08, 0x48, 0xCA, 0xB6, 0x60, 0x66,
0xD1, 0xE7, 0x21, 0xB0, 0x30, 0x06, 0xE4, 0x38, 0x4E, 0xEE, 0x0E, 0xC0, 0x86, 0x31, 0x55, 0xF0,
0xA5, 0xD7, 0x3B, 0xB2, 0x5A, 0xB4, 0x48, 0xC0, 0xA5, 0xF3, 0xBA, 0xFA, 0x2A, 0x03, 0x47, 0xB5,
0x60, 0x88, 0x7F, 0x40, 0x48, 0xA2, 0x65, 0x18, 0x82, 0xDD, 0xDE, 0x00, 0x3D, 0xC9, 0x60, 0xA0,
0xD0, 0x83, 0xE1, 0x99, 0x43, 0xC0, 0x5A, 0xAC, 0x50, 0x1E, 0x4A, 0x91, 0x03, 0x2E, 0x13, 0x8D,
0x22, 0xB6, 0x52, 0xFE, 0xEB, 0xED, 0x9B, 0xDF, 0x39, 0x5F, 0x9C, 0xB1, 0xEB, 0x25, 0x04, 0xB0,
0x7A, 0x30, 0x52, 0x5B, 0x24, 0xCC, 0x2F, 0xC5, 0x6F, 0x0A, 0x46, 0xB0, 0x8D, 0xFD, 0x72, 0xB9,
0x29, 0x5E, 0x48, 0x93, 0x08, 0x30, 0x06, 0x49, 0x6A, 0x36, 0x9B, 0x58, 0xE2, 0x81, 0x70, 0x13,
0xC1, 0x89, 0x52, 0x76, 0x83, 0x45, 0x58, 0x33, 0xFB, 0x70, 0x76, 0x5A, 0xE7, 0x9A, 0xE8, 0x14,
0x35, 0xBE, 0x52, 0x47, 0x39, 0xBA, 0xD3, 0xA3, 0x66, 0x1C, 0xC1, 0xC6, 0xFC, 0x3B, 0x0C, 0x09,
0xD9, 0x04, 0xC2, 0xB3, 0x2B, 0x36, 0xCA, 0x63, 0x20, 0xED, 0xDE, 0x1D, 0x8D, 0xA2, 0x26, 0x0D,
0xC0, 0x88, 0x1A, 0x68, 0x52, 0xB7, 0x4D, 0x13, 0xDB, 0x44, 0x08, 0xF9, 0x72, 0x47, 0x74, 0xFE,
0x1F, 0xE7, 0xEF, 0xFE, 0x00, 0xBF, 0x9B, 0x40, 0x48, 0x8F, 0x53, 0xD3, 0x45, 0x1C, 0xA5, 0xEC,
0x82, 0xDD, 0x72, 0x4D, 0x1B, 0xB8, 0xA6, 0x55, 0x9A, 0x8C, 0xE7, 0x06, 0x06, 0x75, 0x60, 0x77,
0x1A, 0x87, 0xAC, 0x19, 0xC6, 0x57, 0xF5, 0xAC, 0x4B, 0xD3, 0x5F, 0x7F, 0x3C, 0xC1, 0x12, 0x20,
0x10, 0x59, 0x5B, 0x23, 0x96, 0x0B, 0x16, 0xD5, 0xD5, 0xDF, 0x4E, 0x2E, 0x60, 0xCB, 0x3A, 0x44,
0x56, 0xD0, 0x94, 0x02, 0xC9, 0xEB, 0x1B, 0x2C, 0x10, 0x6F, 0x05, 0x24, 0x8F, 0x0F, 0x6A, 0x45,
0xF6, 0xA2, 0x42, 0x13, 0x96, 0x05, 0x53, 0x08, 0x13, 0x9D, 0xB5, 0x70, 0x32, 0xDA, 0xFD, 0xDE,
0xC9, 0xD5, 0x17, 0xB1, 0x5A, 0xB3, 0xF2, 0xCE, 0x35, 0x63, 0xCB, 0x7E, 0xFD, 0x4A, 0xAE, 0x60,
0x0E, 0x59, 0xEC, 0x66, 0xF1, 0xC6, 0x40, 0xBE, 0x3D, 0x08, 0x99, 0x3A, 0x14, 0x89, 0x0D, 0xCA,
0xCD, 0xEB, 0x38, 0x99, 0xBF, 0xF2, 0xB8, 0x37, 0xE4, 0x4D, 0x6F, 0xB1, 0xC0, 0xCD, 0x0A, 0xED,
0x2C, 0xC7, 0xDB, 0x85, 0xAB, 0x8C, 0xC0, 0x55, 0x46, 0x47, 0x19, 0xFE, 0xC3, 0x08, 0x9C, 0xA4,
0x0C, 0xDD, 0xD9, 0xA7, 0xE8, 0x33, 0x58, 0xE1, 0x72, 0x4E, 0x17, 0x48, 0x2B, 0x7A, 0xAE, 0x16,
0xC0, 0x43, 0x3D, 0x10, 0xEE, 0x53, 0x2F, 0xD6, 0x2B, 0x5E, 0xDF, 0x00, 0xD5, 0xB7, 0x01, 0x68,
0xEB, 0x7A, 0xA5, 0xAC, 0xBC, 0x43, 0xDC, 0x35, 0xC9, 0xB9, 0xF7, 0xEF, 0xCE, 0x2F, 0x30, 0xBB,
0x20, 0x78, 0x2A, 0x71, 0xB0, 0x32, 0xB5, 0x29, 0x2F, 0xE0, 0xC3, 0x4E, 0x6E, 0x60, 0x95, 0x37,
0x60, 0xB3, 0x18, 0x08, 0x3D, 0x52, 0x4C, 0xBC, 0x7F, 0x51, 0xF5, 0x22, 0x34, 0xD7, 0xEE, 0x81,
0x5D, 0xD9, 0x5E, 0x41, 0xED, 0x16, 0x4B, 0x2A, 0x6A, 0x66, 0x69, 0x21, 0xCA, 0x92, 0xE7, 0x33,
0xBF, 0x25, 0xD3, 0xB8, 0x7F, 0x83, 0x8C, 0x6A, 0xBF, 0x8A, 0x09, 0x96, 0x08, 0x36, 0x1E, 0xB0,
0x8C, 0x0F, 0xF1, 0x5D, 0x41, 0xC6, 0x37, 0x30, 0xA5, 0x7D, 0x8D, 0xBF, 0x00, 0xA9, 0x9B, 0x1A,
0xE6, 0x60, 0xEB, 0xB5, 0x4E, 0xC1, 0x5E, 0x51, 0xC1, 0xB6, 0x36, 0x36, 0x1E, 0x47, 0xD4, 0x5C,
0xD2, 0x39, 0xD2, 0xB0, 0x51, 0x75, 0x94, 0xD4, 0x98, 0xFA, 0xF3, 0xD0, 0x7B, 0x86, 0x40, 0xCA,
0x77, 0x58, 0x87, 0x5D, 0x4B, 0xA1, 0x44, 0x72, 0x39, 0x18, 0x7F, 0xD0, 0x20, 0x54, 0x37, 0x55,
0x35, 0x0E, 0xDA, 0x40, 0x34, 0x4B, 0xBD, 0x5F, 0x6F, 0xD0, 0x89, 0xD4, 0x9D, 0x6B, 0xEB, 0x42,
0xE1, 0x21, 0xE0, 0xF9, 0x63, 0x39, 0x1F, 0x83, 0x90, 0x90, 0xC5, 0x2D, 0x34, 0x01, 0xD9, 0x2E,
0x63, 0x43, 0xB0, 0xE6, 0x52, 0x4C, 0x8E, 0xF8, 0x50, 0x03, 0x63, 0x6B, 0xAA, 0x8D, 0x28, 0x2B,
0x8A, 0x47, 0x05, 0x2C, 0xD8, 0xDD, 0xFB, 0x5F, 0xD1, 0xD5, 0xE7, 0xD6, 0x03, 0x65, 0x19, 0xD4,
0x8F, 0xE5, 0x15, 0x74, 0xA4, 0xC0, 0x6B, 0x70, 0x0D, 0x7F, 0x63, 0xE8, 0x0F, 0x1B, 0xAA, 0xA1,
0x36, 0x4A, 0x18, 0x60, 0xEF, 0xDB, 0x38, 0xE2, 0x33, 0xE8, 0x82, 0x18, 0x6F, 0x67, 0x3F, 0x82,
0x83, 0x20, 0x65, 0x77, 0xE7, 0xEF, 0x31, 0x64, 0xC8, 0x7B, 0x7B, 0xDF, 0x06, 0xD1, 0x92, 0xB3,
0xFD, 0xFD, 0xE7, 0x0C, 0xCC, 0xA8, 0x2F, 0xFA, 0x8B, 0x5D, 0xFD, 0x1E, 0xF8, 0xEC, 0xE7, 0x30,
0x44, 0x85, 0xC9, 0xDF, 0xC4, 0x98, 0xDB, 0x6F, 0x62, 0x5E, 0xBC, 0xC8, 0xDF, 0x13, 0x35, 0x27,
0x61, 0x8C, 0xB5, 0x98, 0x82, 0xEF, 0xF4, 0x13, 0x89, 0x51, 0xF5, 0xB1, 0xA1, 0xD6, 0x81, 0xCF,
0x13, 0xE1, 0x8A, 0x98, 0xAF, 0x3D, 0x21, 0x3E, 0x62, 0xFB, 0x87, 0x96, 0x8E, 0xEC, 0x64, 0x12,
0x29, 0x7F, 0x40, 0x32, 0x52, 0xF1, 0x17, 0x24, 0x07, 0x56, 0xC9, 0x5F, 0x78, 0xEF, 0x9E, 0x58,
0x50, 0x24, 0x77, 0x2B, 0xF7, 0x19, 0x6D, 0xD4, 0xD7, 0x1E, 0x88, 0xAA, 0x8F, 0xB1, 0x55, 0xF1,
0x26, 0x0B, 0x7F, 0xEC, 0x01, 0x39, 0xC0, 0xEB, 0x8F, 0x3F, 0x94, 0x0B, 0x5C, 0xAF, 0x3F, 0xBE,
0xFB, 0x52, 0x3F, 0xE0, 0x1A, 0xF6, 0x39, 0x6B, 0x76, 0x48, 0x01, 0x0F, 0xED, 0x9A, 0x7E, 0xE7,
0xF8, 0xAC, 0x6D, 0x8B, 0x99, 0x05, 0xEA, 0xA7, 0x51, 0xC0, 0x3F, 0x9C, 0x4A, 0xE1, 0x8E, 0x77,
0xC5, 0x25, 0xA0, 0xBD, 0xAD, 0x89, 0x88, 0x60, 0x5E, 0xCA, 0x2B, 0xEA, 0x65, 0x25, 0x74, 0x50,
0x3F, 0x41, 0xF8, 0xD9, 0x33, 0xCD, 0xCF, 0x10, 0x25, 0xED, 0x79, 0xFF, 0xB7, 0xFD, 0x26, 0xB1,
0xEC, 0xDF, 0x55, 0xC4, 0x43, 0xF9, 0x70, 0x0A, 0x79, 0x41, 0xFC, 0x40, 0xE4, 0x01, 0x16, 0x1E,
0x82, 0x8F, 0xB8, 0x14, 0x7C, 0x64, 0x9A, 0x29, 0x0A, 0x7E, 0x64, 0x25, 0xE3, 0x2C, 0x5E, 0x90,
0x96, 0xDF, 0xD4, 0x23, 0x9A, 0x52, 0xD8, 0x95, 0xEC, 0xDD, 0xDF, 0x8F, 0x80, 0x32, 0xCC, 0x2A,
0x23, 0x53, 0x1D, 0x88, 0x21, 0x85, 0xB4, 0x14, 0x8E, 0x06, 0x8B, 0x98, 0x43, 0x16, 0xA6, 0x2C,
0xB7, 0x2A, 0x01, 0xF8, 0xD7, 0xE0, 0x28, 0x1B, 0x32, 0x0C, 0x32, 0xFF, 0x1A, 0x8E, 0xA2, 0x4F,
0xC1, 0xE7, 0x6C, 0x95, 0x01, 0xAC, 0x02, 0x5C, 0x51, 0x6E, 0x58, 0x92, 0xC2, 0x36, 0x20, 0x47,
0x0D, 0x3F, 0x99, 0x9F, 0x65, 0xB8, 0x08, 0xC1, 0xD3, 0x01, 0x4E, 0x66, 0xA7, 0xE8, 0x2A, 0x02,
0x74, 0xA3, 0x36, 0xC2, 0x4F, 0xD6, 0x67, 0xC8, 0x6B, 0x1A, 0x9A, 0xAE, 0x56, 0x69, 0xBB, 0x05,
0x1C, 0x64, 0x9D, 0xDA, 0xAC, 0xAC, 0xED, 0x65, 0x7D, 0x9B, 0x1B, 0x7B, 0x31, 0x28, 0x4E, 0x6E,
0x1E, 0x70, 0x07, 0x10, 0xAE, 0x6D, 0x82, 0x34, 0x9F, 0x0B, 0x32, 0x0B, 0x79, 0x34, 0xB9, 0xBB,
0x4D, 0x51, 0xDA, 0xDA, 0xDF, 0xC6, 0x2B, 0xE9, 0xF0, 0x93, 0x9D, 0x75, 0x56, 0x5F, 0x4E, 0x87,
0x9F, 0x9C, 0xBC, 0x83, 0x2C, 0xDC, 0x39, 0x75, 0xD5, 0xB3, 0x85, 0x66, 0x71, 0xCA, 0x29, 0xDA,
0xDF, 0xCB, 0x1D, 0x61, 0xF3, 0x4A, 0x94, 0xA4, 0x99, 0x6B, 0x90, 0x47, 0xFE, 0xB2, 0xBE, 0xB7,
0x12, 0xAE, 0x0B, 0xFB, 0xA0, 0x0D, 0x50, 0x7A, 0xD6, 0x6B, 0x14, 0x1F, 0x85, 0x02, 0xDE, 0xB8,
0x1A, 0xF0, 0x62, 0xB7, 0x5E, 0x15, 0x44, 0x29, 0xC5, 0xDA, 0x10, 0x22, 0xEC, 0xDC, 0x44, 0x41,
0xEC, 0x10, 0x97, 0x03, 0x5F, 0x46, 0x61, 0x53, 0xBC, 0x15, 0xF8, 0x56, 0xF6, 0x78, 0xFF, 0x28,
0xEB, 0x5E, 0xCF, 0x9B, 0x46, 0xBB, 0xF5, 0xF8, 0x25, 0x5A, 0x88, 0x8F, 0x6C, 0x2C, 0xC1, 0xAA,
0x2B, 0x3C, 0x88, 0xAF, 0x36, 0xCA, 0x74, 0x6E, 0x80, 0xB4, 0x37, 0xAA, 0x3C, 0x69, 0xA8, 0xAD,
0x15, 0x04, 0x6A, 0x9F, 0x54, 0x2F, 0xF1, 0x97, 0x01, 0x88, 0xE3, 0x67, 0x6D, 0xF0, 0x2C, 0x40,
0x15, 0x10, 0x5A, 0x73, 0x1C, 0x44, 0x90, 0x02, 0x5F, 0xD0, 0x79, 0x09, 0x2F, 0x49, 0xBC, 0xBB,
0xF1, 0x72, 0x3A, 0x65, 0x90, 0x89, 0x15, 0x3B, 0x8B, 0x23, 0xA4, 0xD4, 0xA8, 0x1C, 0x23, 0x56,
0x0C, 0xCF, 0xC7, 0x73, 0x55, 0xDB, 0x75, 0x06, 0xC1, 0x5C, 0x57, 0x80, 0x10, 0x81, 0x2A, 0x50,
0x76, 0x9E, 0x5B, 0xA8, 0x80, 0xFE, 0x1F, 0x82, 0x2D, 0x1D, 0xEC, 0xB7, 0x6F, 0x29, 0xE3, 0x18,
0x48, 0xC4, 0x4B, 0x5E, 0x2F, 0x71, 0x46, 0x77, 0x98, 0xA3, 0x55, 0xD7, 0xA2, 0x33, 0x0E, 0x07,
0x31, 0xC6, 0x24, 0xB6, 0x32, 0x25, 0x3B, 0x25, 0xB1, 0x11, 0x0A, 0xFF, 0x00, 0x41, 0x80, 0x0F,
0xA9, 0x82, 0x12, 0x44, 0xB0, 0x64, 0x34, 0x61, 0x90, 0x28, 0xFF, 0x8C, 0x74, 0xFA, 0x85, 0xE8,
0xA4, 0x15, 0x81, 0x31, 0x8E, 0x2A, 0x1B, 0x2B, 0xFB, 0x68, 0x94, 0x9D, 0x54, 0x40, 0x0B, 0x22,
0x63, 0xFC, 0xD3, 0x57, 0xA0, 0x1B, 0x1C, 0x74, 0x03, 0xDA, 0xB2, 0xD3, 0x1C, 0x1C, 0xED, 0x50,
0xD5, 0x96, 0xBF, 0x52, 0x30, 0x0E, 0xA1, 0x6E, 0x0D, 0xCD, 0x13, 0xFD, 0xAB, 0x1D, 0xA5, 0xB9,
0xB2, 0xEF, 0x07, 0x9A, 0xFB, 0xE2, 0x45, 0xEE, 0x71, 0x4F, 0xB9, 0x92, 0x32, 0x36, 0x4F, 0x95,
0xBB, 0x78, 0xA9, 0xE0, 0x09, 0x34, 0x19, 0x49, 0x28, 0x53, 0xC8, 0xCA, 0x15, 0x2F, 0x8A, 0xC1,
0xC2, 0x80, 0x65, 0x8D, 0x85, 0x28, 0xEA, 0x38, 0x2C, 0xA1, 0x71, 0x51, 0xBC, 0x52, 0xCA, 0x81,
0x07, 0x90, 0x5D, 0x3D, 0x39, 0x3B, 0x7B, 0x77, 0x56, 0xA0, 0xBB, 0x7D, 0xA2, 0x84, 0x83, 0x9D,
0xD8, 0x3C, 0x53, 0xB2, 0xB5, 0x19, 0x1C, 0xD4, 0x50, 0x15, 0xEC, 0x1C, 0xE0, 0x79, 0x0A, 0xEB,
0xB3, 0xA6, 0x57, 0xC2, 0xD1, 0x8D, 0x68, 0xD4, 0x1B, 0xE3, 0xCB, 0x02, 0x4C, 0x4E, 0x4B, 0x01,
0x69, 0x65, 0xC2, 0x3D, 0xA6, 0x93, 0x95, 0x55, 0xC1, 0x1C, 0x87, 0x0C, 0x66, 0xC9, 0xB3, 0x6A,
0xCA, 0x94, 0x02, 0x8F, 0x3A, 0x38, 0xD8, 0xCA, 0xB0, 0x06, 0x58, 0x59, 0xA5, 0xDC, 0x28, 0x37,
0xA2, 0x6D, 0x1D, 0x8C, 0xD1, 0x06, 0xBB, 0x00, 0x42, 0xC8, 0xA2, 0x83, 0xD1, 0x29, 0x52, 0x8E,
0xFF, 0x07, 0x59, 0xC3, 0x5E, 0x9B, 0xAA, 0x0D, 0x32, 0x31, 0x00, 0x67, 0x8D, 0x48, 0x95, 0x0D,
0x5F, 0xF9, 0x24, 0xE0, 0xBD, 0x70, 0xEE, 0xD5, 0xC2, 0xD1, 0xC6, 0x11, 0x47, 0xE5, 0xA5, 0xAA,
0x3D, 0x5C, 0x1C, 0x58, 0x3D, 0xBF, 0x34, 0x80, 0x07, 0x15, 0x9F, 0x47, 0x96, 0xD2, 0xA2, 0xCF,
0x98, 0x8E, 0xE7, 0x10, 0x0F, 0xB9, 0xD8, 0x87, 0x66, 0x96, 0x22, 0x8E, 0x67, 0x46, 0xAA, 0x0F,
0xC4, 0xE7, 0xE2, 0x5C, 0xE5, 0xC1, 0xC2, 0x47, 0x16, 0x68, 0x55, 0x8B, 0x1F, 0xFA, 0x83, 0x75,
0x0D, 0x15, 0xBC, 0xC9, 0xD3, 0xEA, 0x19, 0xA2, 0x9C, 0x51, 0xCC, 0xD3, 0xD6, 0xE5, 0x03, 0x66,
0xFA, 0x93, 0x6B, 0x1A, 0x4B, 0x12, 0x2E, 0xD8, 0xDF, 0x8E, 0xB2, 0xC6, 0x3F, 0xA7, 0x9E, 0x41,
0xB4, 0x7E, 0xB0, 0xA2, 0xB1, 0xC5, 0xFB, 0xC7, 0xD5, 0x32, 0x1E, 0x2A, 0x5F, 0x64, 0xB1, 0xF9,
0xCE, 0x0A, 0xC6, 0xC3, 0xDA, 0xF3, 0x58, 0x13, 0xB4, 0x85, 0xFD, 0x81, 0xD3, 0xB6, 0x07, 0xC0,
0x64, 0xE7, 0x6E, 0x9F, 0xA7, 0x3E, 0xCF, 0xD7, 0xF8, 0xCA, 0xCC, 0xEC, 0xAC, 0xF9, 0xE2, 0xF6,
0x91, 0x36, 0xA2, 0x28, 0xB7, 0xFC, 0x83, 0x2C, 0xCA, 0xF6, 0xF4, 0x12, 0x8E, 0x43, 0x61, 0x42,
0x1F, 0x57, 0xD7, 0x41, 0xF3, 0xA9, 0x5A, 0xF4, 0x22, 0x54, 0x88, 0xC0, 0x8B, 0x17, 0xAA, 0x5B,
0x7D, 0x2C, 0xF7, 0x7E, 0xFB, 0x56, 0x75, 0xBB, 0xAA, 0x0D, 0xBE, 0x3D, 0xEB, 0xD4, 0xAA, 0xFE,
0x6E, 0x82, 0xA1, 0x8E, 0xF0, 0x78, 0x94, 0xAC, 0x29, 0xB8, 0x94, 0x53, 0x1E, 0x2F, 0x14, 0x88,
0x8C, 0xC8, 0x83, 0xFA, 0x82, 0x67, 0x2F, 0x5C, 0x53, 0xE7, 0x23, 0x08, 0xE0, 0xE8, 0xF7, 0xAA,
0x40, 0xE4, 0x7A, 0x49, 0xAE, 0xA3, 0xC6, 0xE8, 0x40, 0xFA, 0x54, 0xD5, 0xBA, 0xE8, 0x31, 0x02,
0x57, 0xC8, 0xAE, 0x6B, 0x19, 0x91, 0xEE, 0x9A, 0x47, 0x11, 0x04, 0x30, 0x13, 0x10, 0xDD, 0x24,
0x5F, 0x9F, 0x63, 0x60, 0xB9, 0xE1, 0x00, 0x21, 0x26, 0xB4, 0x30, 0x98, 0xA4, 0x2D, 0x57, 0x6B,
0x66, 0x3B, 0x9A, 0xF6, 0x95, 0xD1, 0x0A, 0x37, 0x8A, 0x59, 0xC9, 0x7E, 0xB5, 0x2C, 0x4E, 0x98,
0x3F, 0x58, 0x64, 0xA0, 0x13, 0xCA, 0xCF, 0x82, 0x44, 0xEE, 0x63, 0xF8, 0x80, 0x67, 0x0E, 0x0B,
0xFB, 0x90, 0xA7, 0x65, 0x07, 0x46, 0x2F, 0xB6, 0x46, 0xE3, 0xD9, 0x5F, 0x42, 0xE2, 0xE5, 0x87,
0xF3, 0x93, 0xB3, 0x72, 0x65, 0x03, 0x6D, 0x2E, 0xA0, 0x10, 0x71, 0xB0, 0xC5, 0x0D, 0xF5, 0xC5,
0xFB, 0x9F, 0xCF, 0xCF, 0x3F, 0xBE, 0x3B, 0x7B, 0xB5, 0x7B, 0x08, 0xC7, 0x21, 0xE7, 0x1F, 0x7E,
0x79, 0x7B, 0x7A, 0x31, 0xBA, 0xC3, 0x6A, 0x76, 0xB0, 0xC3, 0x49, 0x0C, 0x83, 0x87, 0xDF, 0xAC,
0x04, 0x5B, 0x6F, 0x56, 0x7E, 0x80, 0x36, 0x99, 0x2B, 0x52, 0xE6, 0x18, 0x54, 0x33, 0xC7, 0x3C,
0x35, 0xAC, 0xC4, 0xAE, 0x41, 0x9E, 0x3E, 0x0E, 0xB2, 0x32, 0x0F, 0xB0, 0x3D, 0x28, 0xA7, 0x8E,
0x11, 0xB9, 0xA6, 0x20, 0x4B, 0x1D, 0x57, 0x41, 0xE4, 0xC7, 0xAB, 0x1D, 0xB6, 0x3B, 0x9B, 0xBF,
0x1E, 0x1E, 0xB5, 0xE4, 0x31, 0xF2, 0xA3, 0x96, 0xFC, 0xF5, 0x0A, 0xFD, 0xA3, 0xD1, 0xFF, 0x0B,
0xB4, 0xEA, 0x2E, 0x52, 0x3B, 0x5A, 0x00, 0x00
};
#endif //__nofile_h

View File

@ -0,0 +1,411 @@
/*
notifications_service.cpp - notifications service functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//Inspired by following sources
//* Line :
// - https://github.com/TridentTD/TridentTD_LineNotify
// - https://notify-bot.line.me/doc/en/
//* Pushover:
// - https://github.com/ArduinoHannover/Pushover
// - https://pushover.net/api
//* Email:
// - https://github.com/CosmicBoris/ESP8266SMTP
// - https://www.electronicshub.org/send-an-email-using-esp8266/
#include "config.h"
#ifdef ENABLE_NOTIFICATIONS
#include "grbl.h"
#include "commands.h"
#include "notifications_service.h"
#include <WiFiClientSecure.h>
#include <Preferences.h>
#include <base64.h>
#include "wificonfig.h"
#define PUSHOVERTIMEOUT 5000
#define PUSHOVERSERVER "api.pushover.net"
#define PUSHOVERPORT 443
#define LINETIMEOUT 5000
#define LINESERVER "notify-api.line.me"
#define LINEPORT 443
#define EMAILTIMEOUT 5000
NotificationsService notificationsservice;
bool Wait4Answer(WiFiClientSecure & client, const char * linetrigger, const char * expected_answer, uint32_t timeout)
{
if(client.connected()) {
String answer;
uint32_t starttimeout = millis();
while (client.connected() && ((millis() -starttimeout) < timeout)) {
answer = client.readStringUntil('\n');
log_d("Answer: %s", answer.c_str());
if ((answer.indexOf(linetrigger) != -1) || (strlen(linetrigger) == 0)) {
break;
}
COMMANDS::wait(10);
}
if (strlen(expected_answer) == 0) {
log_d("Answer ignored as requested");
return true;
}
if(answer.indexOf(expected_answer) == -1) {
log_d("Did not got answer!");
return false;
} else {
log_d("Got expected answer");
return true;
}
}
log_d("Failed to send message");
return false;
}
NotificationsService::NotificationsService()
{
_started = false;
_notificationType = 0;
_token1 = "";
_token1 = "";
_settings = "";
}
NotificationsService::~NotificationsService()
{
end();
}
bool NotificationsService::started()
{
return _started;
}
const char * NotificationsService::getTypeString()
{
switch(_notificationType) {
case ESP_PUSHOVER_NOTIFICATION:
return "Pushover";
case ESP_EMAIL_NOTIFICATION:
return "Email";
case ESP_LINE_NOTIFICATION:
return "Line";
default:
break;
}
return "None";
}
bool NotificationsService::sendMSG(const char * title, const char * message)
{
if (!_started) return false;
if (!((strlen(title) == 0) && (strlen(message) == 0))) {
switch(_notificationType) {
case ESP_PUSHOVER_NOTIFICATION:
return sendPushoverMSG(title,message);
break;
case ESP_EMAIL_NOTIFICATION:
return sendEmailMSG(title,message);
break;
case ESP_LINE_NOTIFICATION :
return sendLineMSG(title,message);
break;
default:
break;
}
}
return false;
}
//Messages are currently limited to 1024 4-byte UTF-8 characters
//but we do not do any check
bool NotificationsService::sendPushoverMSG(const char * title, const char * message)
{
String data;
String postcmd;
bool res;
WiFiClientSecure Notificationclient;
if (!Notificationclient.connect(_serveraddress.c_str(), _port)) {
log_d("Error connecting server %s:%d", _serveraddress.c_str(), _port);
return false;
}
//build data for post
data = "user=";
data += _token1;
data += "&token=";
data += _token2;;
data +="&title=";
data += title;
data += "&message=";
data += message;
data += "&device=";
data += wifi_config.Hostname();
//build post query
postcmd = "POST /1/messages.json HTTP/1.1\r\nHost: api.pushover.net\r\nConnection: close\r\nCache-Control: no-cache\r\nUser-Agent: ESP3D\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nContent-Length: ";
postcmd += data.length();
postcmd +="\r\n\r\n";
postcmd +=data;
log_d("Query: %s", postcmd.c_str());
//send query
Notificationclient.print(postcmd);
res = Wait4Answer(Notificationclient, "{", "\"status\":1", PUSHOVERTIMEOUT);
Notificationclient.stop();
return res;
}
bool NotificationsService::sendEmailMSG(const char * title, const char * message)
{
WiFiClientSecure Notificationclient;
log_d("Connect to server");
if (!Notificationclient.connect(_serveraddress.c_str(), _port)) {
log_d("Error connecting server %s:%d", _serveraddress.c_str(), _port);
return false;
}
//Check answer of connection
if(!Wait4Answer(Notificationclient, "220", "220", EMAILTIMEOUT)) {
log_d("Connection failed!");
return false;
}
//Do HELO
log_d("HELO");
Notificationclient.print("HELO friend\r\n");
if(!Wait4Answer(Notificationclient, "250", "250", EMAILTIMEOUT)) {
log_d("HELO failed!");
return false;
}
log_d("AUTH LOGIN");
//Request AUthentication
Notificationclient.print("AUTH LOGIN\r\n");
if(!Wait4Answer(Notificationclient, "334", "334", EMAILTIMEOUT)) {
log_d("AUTH LOGIN failed!");
return false;
}
log_d("Send LOGIN");
//sent Login
Notificationclient.printf("%s\r\n",_token1.c_str());
if(!Wait4Answer(Notificationclient, "334", "334", EMAILTIMEOUT)) {
log_d("Sent login failed!");
return false;
}
log_d("Send PASSWORD");
//Send password
Notificationclient.printf("%s\r\n",_token2.c_str());
if(!Wait4Answer(Notificationclient, "235", "235", EMAILTIMEOUT)) {
log_d("Sent password failed!");
return false;
}
log_d("MAIL FROM");
//Send From
Notificationclient.printf("MAIL FROM: <%s>\r\n",_settings.c_str());
if(!Wait4Answer(Notificationclient, "250", "250", EMAILTIMEOUT)) {
log_d("MAIL FROM failed!");
return false;
}
log_d("RCPT TO");
//Send To
Notificationclient.printf("RCPT TO: <%s>\r\n",_settings.c_str());
if(!Wait4Answer(Notificationclient, "250", "250", EMAILTIMEOUT)) {
log_d("RCPT TO failed!");
return false;
}
log_d("DATA");
//Send Data
Notificationclient.print("DATA\r\n");
if(!Wait4Answer(Notificationclient, "354", "354", EMAILTIMEOUT)) {
log_d("Preparing DATA failed!");
return false;
}
log_d("Send message");
//Send message
Notificationclient.printf("From:ESP3D<%s>\r\n",_settings.c_str());
Notificationclient.printf("To: <%s>\r\n",_settings.c_str());
Notificationclient.printf("Subject: %s\r\n\r\n",title);
Notificationclient.println(message);
log_d("Send final dot");
//Send Final dot
Notificationclient.print(".\r\n");
if(!Wait4Answer(Notificationclient, "250", "250", EMAILTIMEOUT)) {
log_d("Sending final dot failed!");
return false;
}
log_d("QUIT");
//Quit
Notificationclient.print("QUIT\r\n");
if(!Wait4Answer(Notificationclient, "221", "221", EMAILTIMEOUT)) {
log_d("QUIT failed!");
return false;
}
Notificationclient.stop();
return true;
}
bool NotificationsService::sendLineMSG(const char * title, const char * message)
{
String data;
String postcmd;
bool res;
WiFiClientSecure Notificationclient;
(void)title;
if (!Notificationclient.connect(_serveraddress.c_str(), _port)) {
log_d("Error connecting server %s:%d", _serveraddress.c_str(), _port);
return false;
}
//build data for post
data = "message=";
data += message;
//build post query
postcmd = "POST /api/notify HTTP/1.1\r\nHost: notify-api.line.me\r\nConnection: close\r\nCache-Control: no-cache\r\nUser-Agent: ESP3D\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nContent-Type: application/x-www-form-urlencoded\r\n";
postcmd +="Authorization: Bearer ";
postcmd += _token1 + "\r\n";
postcmd += "Content-Length: ";
postcmd += data.length();
postcmd +="\r\n\r\n";
postcmd +=data;
log_d("Query: %s", postcmd.c_str());
//send query
Notificationclient.print(postcmd);
res = Wait4Answer(Notificationclient, "{", "\"status\":200", LINETIMEOUT);
Notificationclient.stop();
return res;
}
//Email#serveraddress:port
bool NotificationsService::getPortFromSettings()
{
Preferences prefs;
String defV = DEFAULT_TOKEN;
prefs.begin(NAMESPACE, true);
String tmp = prefs.getString(NOTIFICATION_TS, defV);
prefs.end();
int pos = tmp.lastIndexOf(':');
if (pos == -1) {
return false;
}
_port= tmp.substring(pos+1).toInt();
log_d("port : %d", _port);
if (_port > 0) {
return true;
} else {
return false;
}
}
//Email#serveraddress:port
bool NotificationsService::getServerAddressFromSettings()
{
Preferences prefs;
String defV = DEFAULT_TOKEN;
prefs.begin(NAMESPACE, true);
String tmp = prefs.getString(NOTIFICATION_TS, defV);
prefs.end();
int pos1 = tmp.indexOf('#');
int pos2 = tmp.lastIndexOf(':');
if ((pos1 == -1) || (pos2 == -1)) {
return false;
}
//TODO add a check for valid email ?
_serveraddress = tmp.substring(pos1+1, pos2);
log_d("server : %s", _serveraddress.c_str());
return true;
}
//Email#serveraddress:port
bool NotificationsService::getEmailFromSettings()
{
Preferences prefs;
String defV = DEFAULT_TOKEN;
prefs.begin(NAMESPACE, true);
String tmp = prefs.getString(NOTIFICATION_TS, defV);
prefs.end();
int pos = tmp.indexOf('#');
if (pos == -1) {
return false;
}
_settings = tmp.substring(0, pos);
log_d("email : %s", _settings.c_str());
//TODO add a check for valid email ?
return true;
}
bool NotificationsService::begin()
{
bool res = true;
end();
Preferences prefs;
String defV = DEFAULT_TOKEN;
prefs.begin(NAMESPACE, true);
_notificationType = prefs.getChar(NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_TYPE);
switch(_notificationType) {
case 0: //no notification = no error but no start
return true;
case ESP_PUSHOVER_NOTIFICATION:
_token1 = prefs.getString(NOTIFICATION_T1, defV);
_token2 = prefs.getString(NOTIFICATION_T2, defV);
_port = PUSHOVERPORT;
_serveraddress = PUSHOVERSERVER;
break;
case ESP_LINE_NOTIFICATION:
_token1 = prefs.getString(NOTIFICATION_T1, defV);
_port = LINEPORT;
_serveraddress = LINESERVER;
break;
case ESP_EMAIL_NOTIFICATION:
_token1 = base64::encode(prefs.getString(NOTIFICATION_T1, defV));
_token2 = base64::encode(prefs.getString(NOTIFICATION_T2, defV));
//log_d("%s",Settings_ESP3D::read_string(ESP_NOTIFICATION_TOKEN1));
//log_d("%s",Settings_ESP3D::read_string(ESP_NOTIFICATION_TOKEN2));
if(!getEmailFromSettings() || !getPortFromSettings()|| !getServerAddressFromSettings()) {
prefs.end();
return false;
}
break;
default:
prefs.end();
return false;
break;
}
prefs.end();
if(WiFi.getMode() != WIFI_STA){
res = false;
}
if (!res) {
end();
}
_started = res;
return _started;
}
void NotificationsService::end()
{
if(!_started) {
return;
}
_started = false;
_notificationType = 0;
_token1 = "";
_token1 = "";
_settings = "";
_serveraddress = "";
_port = 0;
}
void NotificationsService::handle()
{
if (_started) {
}
}
#endif //ENABLE_NOTIFICATIONS

View File

@ -0,0 +1,57 @@
/*
notifications_service.h - notifications service functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _NOTIFICATIONS_SERVICE_H
#define _NOTIFICATIONS_SERVICE_H
class NotificationsService
{
public:
NotificationsService();
~NotificationsService();
bool begin();
void end();
void handle();
bool sendMSG(const char * title, const char * message);
const char * getTypeString();
bool started();
private:
bool _started;
uint8_t _notificationType;
String _token1;
String _token2;
String _settings;
String _serveraddress;
uint16_t _port;
bool sendPushoverMSG(const char * title, const char * message);
bool sendEmailMSG(const char * title, const char * message);
bool sendLineMSG(const char * title, const char * message);
bool getPortFromSettings();
bool getServerAddressFromSettings();
bool getEmailFromSettings();
};
extern NotificationsService notificationsservice;
#endif //_NOTIFICATIONS_SERVICE_H

View File

@ -0,0 +1,189 @@
/*
nuts_bolts.c - Shared functions
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
// Extracts a floating point value from a string. The following code is based loosely on
// the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely
// available conversion method examples, but has been highly optimized for Grbl. For known
// CNC applications, the typical decimal value is expected to be in the range of E0 to E-4.
// 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.
// 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)
{
char *ptr = line + *char_counter;
unsigned char c;
// Grab first character and increment pointer. No spaces assumed in line.
c = *ptr++;
// Capture initial positive/minus character
bool isnegative = false;
if (c == '-') {
isnegative = true;
c = *ptr++;
} else if (c == '+') {
c = *ptr++;
}
// Extract number into fast integer. Track decimal in terms of exponent value.
uint32_t intval = 0;
int8_t exp = 0;
uint8_t ndigit = 0;
bool isdecimal = false;
while(1) {
c -= '0';
if (c <= 9) {
ndigit++;
if (ndigit <= MAX_INT_DIGITS) {
if (isdecimal) { exp--; }
intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c
} else {
if (!(isdecimal)) { exp++; } // Drop overflow digits
}
} else if (c == (('.'-'0') & 0xff) && !(isdecimal)) {
isdecimal = true;
} else {
break;
}
c = *ptr++;
}
// Return if no digits have been read.
if (!ndigit) { return(false); };
// Convert integer into floating point.
float fval;
fval = (float)intval;
// Apply decimal. Should perform no more than two floating point multiplications for the
// expected range of E0 to E-4.
if (fval != 0) {
while (exp <= -2) {
fval *= 0.01;
exp += 2;
}
if (exp < 0) {
fval *= 0.1;
} else if (exp > 0) {
do {
fval *= 10.0;
} while (--exp > 0);
}
}
// Assign floating point value with correct sign.
if (isnegative) {
*float_ptr = -fval;
} else {
*float_ptr = fval;
}
*char_counter = ptr - line - 1; // Set char_counter to next statement
return(true);
}
void delay_ms(uint16_t ms)
{
delay(ms);
}
// Non-blocking delay function used for general operation and suspend features.
void delay_sec(float seconds, uint8_t mode)
{
uint16_t i = ceil(1000/DWELL_TIME_STEP*seconds);
while (i-- > 0) {
if (sys.abort) { return; }
if (mode == DELAY_MODE_DWELL) {
protocol_execute_realtime();
} else { // DELAY_MODE_SYS_SUSPEND
// Execute rt_system() only to avoid nesting suspend loops.
protocol_exec_rt_system();
if (sys.suspend & SUSPEND_RESTART_RETRACT) { return; } // Bail, if safety door reopens.
}
delay(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment
}
}
// Simple hypotenuse computation function.
float hypot_f(float x, float y) { return(sqrt(x*x + y*y)); }
float convert_delta_vector_to_unit_vector(float *vector)
{
uint8_t idx;
float magnitude = 0.0;
for (idx=0; idx<N_AXIS; idx++) {
if (vector[idx] != 0.0) {
magnitude += vector[idx]*vector[idx];
}
}
magnitude = sqrt(magnitude);
float inv_magnitude = 1.0/magnitude;
for (idx=0; idx<N_AXIS; idx++) { vector[idx] *= inv_magnitude; }
return(magnitude);
}
float limit_value_by_axis_maximum(float *max_value, 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(max_value[idx]/unit_vec[idx]));
}
}
return(limit_value);
}
float map_float(float x, float in_min, float in_max, float out_min, float out_max) // DrawBot_Badge
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float constrain_float(float in, float min, float max) // DrawBot_Badge
{
if (in < min)
return min;
if (in > max)
return max;
return in;
}
float mapConstrain(float x, float in_min, float in_max, float out_min, float out_max)
{
x = constrain_float(x, in_min, in_max);
return map_float(x, in_min, in_max, out_min, out_max);
}

View File

@ -0,0 +1,99 @@
/*
nuts_bolts.h - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 nuts_bolts_h
#define nuts_bolts_h
#include "config.h"
#define false 0
#define true 1
#define SOME_LARGE_VALUE 1.0E+38
// Axis array index values. Must start with 0 and be continuous.
// Note: You set the number of axes used by changing N_AXIS.
// Be sure to define pins or servos in cpu_map.h
#define X_AXIS 0 // Axis indexing value.
#define Y_AXIS 1
#define Z_AXIS 2
#define A_AXIS 3
#define B_AXIS 4
#define C_AXIS 5
// CoreXY motor assignments. DO NOT ALTER.
// NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations.
#define A_MOTOR X_AXIS // Must be X_AXIS
#define B_MOTOR Y_AXIS // Must be Y_AXIS
// Conversions
#define MM_PER_INCH (25.40)
#define INCH_PER_MM (0.0393701)
#define TICKS_PER_MICROSECOND (F_STEPPER_TIMER/1000000) // Different from AVR version
#define DELAY_MODE_DWELL 0
#define DELAY_MODE_SYS_SUSPEND 1
// Useful macros
#define clear_vector(a) memset(a, 0, sizeof(a))
#define clear_vector_float(a) memset(a, 0.0, sizeof(float)*N_AXIS)
// #define clear_vector_long(a) memset(a, 0.0, sizeof(long)*N_AXIS)
#define MAX(a,b) (((a) > (b)) ? (a) : (b)) // changed to upper case to remove conflicts with other libraries
#define MIN(a,b) (((a) < (b)) ? (a) : (b)) // changed to upper case to remove conflicts with other libraries
#define isequal_position_vector(a,b) !(memcmp(a, b, sizeof(float)*N_AXIS))
// Bit field and masking macros
//Arduino.h:104:0: note: this is the location of the previous definition
//#define bit(n) (1 << n)
#define bit_true(x,mask) (x) |= (mask)
#define bit_false(x,mask) (x) &= ~(mask)
#define bit_istrue(x,mask) ((x & mask) != 0)
#define bit_isfalse(x,mask) ((x & mask) == 0)
// 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
// a pointer to the result variable. Returns true when it succeeds
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr);
// Non-blocking delay function used for general operation and suspend features.
void delay_sec(float seconds, uint8_t mode);
// Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms().
void delay_ms(uint16_t ms);
// Computes hypotenuse, avoiding avr-gcc's bloated version and the extra error checking.
float hypot_f(float x, float y);
float convert_delta_vector_to_unit_vector(float *vector);
float limit_value_by_axis_maximum(float *max_value, float *unit_vec);
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 constrain_float(float in, float min, float max);
template <class T> void swap ( T& a, T& b )
{
T c(a); a=b; b=c;
}
#endif

View File

@ -0,0 +1,511 @@
/*
planner.c - buffers movement commands and manages the acceleration profile plan
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Jens Geisler
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
#include <stdlib.h> // PSoc Required for labs
static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
static uint8_t block_buffer_tail; // Index of the block to process now
static uint8_t block_buffer_head; // Index of the next block to be pushed
static uint8_t next_buffer_head; // Index of the next buffer head
static uint8_t block_buffer_planned; // Index of the optimally planned block
// Define planner variables
typedef struct {
int32_t position[N_AXIS]; // The planner position of the tool in absolute steps. Kept separate
// from g-code position for movements requiring multiple line motions,
// i.e. arcs, canned cycles, and backlash compensation.
float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment
float previous_nominal_speed; // Nominal speed of previous path line segment
} planner_t;
static planner_t pl;
// Returns the index of the next block in the ring buffer. Also called by stepper segment buffer.
uint8_t plan_next_block_index(uint8_t block_index)
{
block_index++;
if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; }
return(block_index);
}
// Returns the index of the previous block in the ring buffer
static uint8_t plan_prev_block_index(uint8_t block_index)
{
if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; }
block_index--;
return(block_index);
}
/* PLANNER SPEED DEFINITION
+--------+ <- current->nominal_speed
/ \
current->entry_speed -> + \
| + <- next->entry_speed (aka exit speed)
+-------------+
time -->
Recalculates the motion plan according to the following basic guidelines:
1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
(i.e. current->entry_speed) such that:
a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
neighboring blocks.
b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
with a maximum allowable deceleration over the block travel distance.
c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero).
2. Go over every block in chronological (forward) order and dial down junction speed values if
a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
acceleration over the block travel distance.
When these stages are complete, the planner will have maximized the velocity profiles throughout the all
of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements
are possible. If a new block is added to the buffer, the plan is recomputed according to the said
guidelines for a new optimal plan.
To increase computational efficiency of these guidelines, a set of planner block pointers have been
created to indicate stop-compute points for when the planner guidelines cannot logically make any further
changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
point) are all accelerating, they are all optimal and can not be altered by a new block added to the
planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
recomputed as stated in the general guidelines.
Planner buffer index mapping:
- block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
- block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
the buffer is full or empty. As described for standard ring buffers, this block is always empty.
- next_buffer_head: Points to next planner buffer block after the buffer head block. When equal to the
buffer tail, this indicates the buffer is full.
- block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
planner buffer that don't change with the addition of a new block, as describe above. In addition,
this block can never be less than block_buffer_tail and will always be pushed forward and maintain
this requirement when encountered by the plan_discard_current_block() routine during a cycle.
NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short
line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't
enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and then
decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this happens and
becomes an annoyance, there are a few simple solutions: (1) Maximize the machine acceleration. The planner
will be able to compute higher velocity profiles within the same combined distance. (2) Maximize line
motion(s) distance per block to a desired tolerance. The more combined distance the planner has to use,
the faster it can go. (3) Maximize the planner buffer size. This also will increase the combined distance
for the planner to compute over. It also increases the number of computations the planner has to perform
to compute an optimal plan, so select carefully. The Arduino 328p memory is already maxed out, but future
ARM versions should have enough memory and speed for look-ahead blocks numbering up to a hundred or more.
*/
static void planner_recalculate()
{
// Initialize block index to the last block in the planner buffer.
uint8_t block_index = plan_prev_block_index(block_buffer_head);
// Bail. Can't do anything with one only one plan-able block.
if (block_index == block_buffer_planned) { return; }
// Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
// block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
// NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan.
float entry_speed_sqr;
plan_block_t *next;
plan_block_t *current = &block_buffer[block_index];
// Calculate maximum entry speed for last block in buffer, where the exit speed is always zero.
current->entry_speed_sqr = MIN( current->max_entry_speed_sqr, 2*current->acceleration*current->millimeters);
block_index = plan_prev_block_index(block_index);
if (block_index == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete.
// Check if the first block is the tail. If so, notify stepper to update its current parameters.
if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
} else { // Three or more plan-able blocks
while (block_index != block_buffer_planned) {
next = current;
current = &block_buffer[block_index];
block_index = plan_prev_block_index(block_index);
// Check if next block is the tail block(=planned block). If so, update current stepper parameters.
if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
// Compute maximum entry speed decelerating over the current block from its exit speed.
if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
entry_speed_sqr = next->entry_speed_sqr + 2*current->acceleration*current->millimeters;
if (entry_speed_sqr < current->max_entry_speed_sqr) {
current->entry_speed_sqr = entry_speed_sqr;
} else {
current->entry_speed_sqr = current->max_entry_speed_sqr;
}
}
}
}
// Forward Pass: Forward plan the acceleration curve from the planned pointer onward.
// Also scans for optimal plan breakpoints and appropriately updates the planned pointer.
next = &block_buffer[block_buffer_planned]; // Begin at buffer planned pointer
block_index = plan_next_block_index(block_buffer_planned);
while (block_index != block_buffer_head) {
current = next;
next = &block_buffer[block_index];
// Any acceleration detected in the forward pass automatically moves the optimal planned
// pointer forward, since everything before this is all optimal. In other words, nothing
// can improve the plan from the buffer tail to the planned pointer by logic.
if (current->entry_speed_sqr < next->entry_speed_sqr) {
entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters;
// If true, current block is full-acceleration and we can move the planned pointer forward.
if (entry_speed_sqr < next->entry_speed_sqr) {
next->entry_speed_sqr = entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this.
block_buffer_planned = block_index; // Set optimal plan pointer.
}
}
// Any block set at its maximum entry speed also creates an optimal plan up to this
// point in the buffer. When the plan is bracketed by either the beginning of the
// buffer and a maximum entry speed or two maximum entry speeds, every block in between
// cannot logically be further improved. Hence, we don't have to recompute them anymore.
if (next->entry_speed_sqr == next->max_entry_speed_sqr) { block_buffer_planned = block_index; }
block_index = plan_next_block_index( block_index );
}
}
void plan_reset()
{
memset(&pl, 0, sizeof(planner_t)); // Clear planner struct
plan_reset_buffer();
}
void plan_reset_buffer()
{
block_buffer_tail = 0;
block_buffer_head = 0; // Empty = tail
next_buffer_head = 1; // plan_next_block_index(block_buffer_head)
block_buffer_planned = 0; // = block_buffer_tail;
}
void plan_discard_current_block()
{
if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer.
uint8_t block_index = plan_next_block_index( block_buffer_tail );
// Push block_buffer_planned pointer, if encountered.
if (block_buffer_tail == block_buffer_planned) { block_buffer_planned = block_index; }
block_buffer_tail = block_index;
}
}
// Returns address of planner buffer block used by system motions. Called by segment generator.
plan_block_t *plan_get_system_motion_block()
{
return(&block_buffer[block_buffer_head]);
}
// Returns address of first planner block, if available. Called by various main program functions.
plan_block_t *plan_get_current_block()
{
if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty
return(&block_buffer[block_buffer_tail]);
}
float plan_get_exec_block_exit_speed_sqr()
{
uint8_t block_index = plan_next_block_index(block_buffer_tail);
if (block_index == block_buffer_head) { return( 0.0 ); }
return( block_buffer[block_index].entry_speed_sqr );
}
// Returns the availability status of the block ring buffer. True, if full.
uint8_t plan_check_full_buffer()
{
if (block_buffer_tail == next_buffer_head) { return(true); }
return(false);
}
// Computes and returns block nominal speed based on running condition and override values.
// NOTE: All system motion commands, such as homing/parking, are not subject to overrides.
float plan_compute_profile_nominal_speed(plan_block_t *block)
{
float nominal_speed = block->programmed_rate;
if (block->condition & PL_COND_FLAG_RAPID_MOTION) { nominal_speed *= (0.01*sys.r_override); }
else {
if (!(block->condition & PL_COND_FLAG_NO_FEED_OVERRIDE)) { nominal_speed *= (0.01*sys.f_override); }
if (nominal_speed > block->rapid_rate) { nominal_speed = block->rapid_rate; }
}
if (nominal_speed > MINIMUM_FEED_RATE) { return(nominal_speed); }
return(MINIMUM_FEED_RATE);
}
// Computes and updates the max entry speed (sqr) of the block, based on the minimum of the junction's
// previous and current nominal speeds and max junction speed.
static void plan_compute_profile_parameters(plan_block_t *block, float nominal_speed, float prev_nominal_speed)
{
// Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds.
if (nominal_speed > prev_nominal_speed) { block->max_entry_speed_sqr = prev_nominal_speed*prev_nominal_speed; }
else { block->max_entry_speed_sqr = nominal_speed*nominal_speed; }
if (block->max_entry_speed_sqr > block->max_junction_speed_sqr) { block->max_entry_speed_sqr = block->max_junction_speed_sqr; }
}
// Re-calculates buffered motions profile parameters upon a motion-based override change.
void plan_update_velocity_profile_parameters()
{
uint8_t block_index = block_buffer_tail;
plan_block_t *block;
float nominal_speed;
float prev_nominal_speed = SOME_LARGE_VALUE; // Set high for first block nominal speed calculation.
while (block_index != block_buffer_head) {
block = &block_buffer[block_index];
nominal_speed = plan_compute_profile_nominal_speed(block);
plan_compute_profile_parameters(block, nominal_speed, prev_nominal_speed);
prev_nominal_speed = nominal_speed;
block_index = plan_next_block_index(block_index);
}
pl.previous_nominal_speed = prev_nominal_speed; // Update prev nominal speed for next incoming block.
}
uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
{
// Prepare and initialize new block. Copy relevant pl_data for block execution.
plan_block_t *block = &block_buffer[block_buffer_head];
memset(block,0,sizeof(plan_block_t)); // Zero all block values.
block->condition = pl_data->condition;
#ifdef VARIABLE_SPINDLE
block->spindle_speed = pl_data->spindle_speed;
#endif
#ifdef USE_LINE_NUMBERS
block->line_number = pl_data->line_number;
#endif
// Compute and store initial move distance data.
int32_t target_steps[N_AXIS], position_steps[N_AXIS];
float unit_vec[N_AXIS], delta_mm;
uint8_t idx;
// Copy position data based on type of motion being planned.
if (block->condition & PL_COND_FLAG_SYSTEM_MOTION) {
#ifdef COREXY
position_steps[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
position_steps[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
position_steps[Z_AXIS] = sys_position[Z_AXIS];
#else
memcpy(position_steps, sys_position, sizeof(sys_position));
#endif
} else { memcpy(position_steps, pl.position, sizeof(pl.position)); }
#ifdef COREXY
target_steps[A_MOTOR] = lround(target[A_MOTOR]*settings.steps_per_mm[A_MOTOR]);
target_steps[B_MOTOR] = lround(target[B_MOTOR]*settings.steps_per_mm[B_MOTOR]);
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]));
#endif
for (idx=0; idx<N_AXIS; idx++) {
// Calculate target position in absolute steps, number of steps for each axis, and determine max step events.
// Also, compute individual axes distance for move and prep unit vector calculations.
// NOTE: Computes true distance from converted step values.
#ifdef COREXY
if ( !(idx == A_MOTOR) && !(idx == B_MOTOR) ) {
target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
block->steps[idx] = labs(target_steps[idx]-position_steps[idx]);
}
block->step_event_count = MAX(block->step_event_count, block->steps[idx]);
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];
} 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];
} else {
delta_mm = (target_steps[idx] - position_steps[idx])/settings.steps_per_mm[idx];
}
#else
target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]);
block->steps[idx] = labs(target_steps[idx]-position_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];
#endif
unit_vec[idx] = delta_mm; // Store unit vector numerator
// Set direction bits. Bit enabled always means direction is negative.
if (delta_mm < 0.0 ) { block->direction_bits |= get_direction_pin_mask(idx); }
}
// Bail if this is a zero-length block. Highly unlikely to occur.
if (block->step_event_count == 0) { return(PLAN_EMPTY_BLOCK); }
// Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
// down such that no individual axes maximum values are exceeded with respect to the line direction.
// 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.
block->millimeters = convert_delta_vector_to_unit_vector(unit_vec);
block->acceleration = limit_value_by_axis_maximum(settings.acceleration, unit_vec);
block->rapid_rate = limit_value_by_axis_maximum(settings.max_rate, unit_vec);
// Store programmed rate.
if (block->condition & PL_COND_FLAG_RAPID_MOTION) { block->programmed_rate = block->rapid_rate; }
else {
block->programmed_rate = pl_data->feed_rate;
if (block->condition & PL_COND_FLAG_INVERSE_TIME) { block->programmed_rate *= block->millimeters; }
}
// TODO: Need to check this method handling zero junction speeds when starting from rest.
if ((block_buffer_head == block_buffer_tail) || (block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
// Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later.
// If system motion, the system motion block always is assumed to start from rest and end at a complete stop.
block->entry_speed_sqr = 0.0;
block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity.
} else {
// Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
// Let a circle be tangent to both previous and current path line segments, where the junction
// deviation is defined as the distance from the junction to the closest edge of the circle,
// colinear with the circle center. The circular segment joining the two paths represents the
// path of centripetal acceleration. Solve for max velocity based on max acceleration about the
// radius of the circle, defined indirectly by junction deviation. This may be also viewed as
// path width or max_jerk in the previous Grbl version. This approach does not actually deviate
// from path, but used as a robust way to compute cornering speeds, as it takes into account the
// nonlinearities of both the junction angle and junction velocity.
//
// NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
// mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
// stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
// is exactly the same. Instead of motioning all the way to junction point, the machine will
// just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
// a continuous mode path, but ARM-based microcontrollers most certainly do.
//
// NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be
// changed dynamically during operation nor can the line move geometry. This must be kept in
// memory in the event of a feedrate override changing the nominal speeds of blocks, which can
// change the overall maximum entry speed conditions of all blocks.
float junction_unit_vec[N_AXIS];
float junction_cos_theta = 0.0;
for (idx=0; idx<N_AXIS; idx++) {
junction_cos_theta -= pl.previous_unit_vec[idx]*unit_vec[idx];
junction_unit_vec[idx] = unit_vec[idx]-pl.previous_unit_vec[idx];
}
// NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
if (junction_cos_theta > 0.999999) {
// For a 0 degree acute junction, just set minimum junction speed.
block->max_junction_speed_sqr = MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED;
} else {
if (junction_cos_theta < -0.999999) {
// Junction is a straight line or 180 degrees. Junction speed is infinite.
block->max_junction_speed_sqr = SOME_LARGE_VALUE;
} else {
convert_delta_vector_to_unit_vector(junction_unit_vec);
float junction_acceleration = limit_value_by_axis_maximum(settings.acceleration, junction_unit_vec);
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,
(junction_acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) );
}
}
}
// Block system motion from updating this data to ensure next g-code motion is computed correctly.
if (!(block->condition & PL_COND_FLAG_SYSTEM_MOTION)) {
float nominal_speed = plan_compute_profile_nominal_speed(block);
plan_compute_profile_parameters(block, nominal_speed, pl.previous_nominal_speed);
pl.previous_nominal_speed = nominal_speed;
// Update previous path unit_vector and planner position.
memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[]
memcpy(pl.position, target_steps, sizeof(target_steps)); // pl.position[] = target_steps[]
// New block is all set. Update buffer head and next buffer head indices.
block_buffer_head = next_buffer_head;
next_buffer_head = plan_next_block_index(block_buffer_head);
// Finish up by recalculating the plan with the new block.
planner_recalculate();
}
return(PLAN_OK);
}
// Reset the planner position vectors. Called by the system abort/initialization routine.
void plan_sync_position()
{
// TODO: For motor configurations not in the same coordinate frame as the machine position,
// this function needs to be updated to accomodate the difference.
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
#ifdef COREXY
if (idx==X_AXIS) {
pl.position[X_AXIS] = system_convert_corexy_to_x_axis_steps(sys_position);
} else if (idx==Y_AXIS) {
pl.position[Y_AXIS] = system_convert_corexy_to_y_axis_steps(sys_position);
} else {
pl.position[idx] = sys_position[idx];
}
#else
pl.position[idx] = sys_position[idx];
#endif
}
}
// Returns the number of available blocks are in the planner buffer.
uint8_t plan_get_block_buffer_available()
{
if (block_buffer_head >= block_buffer_tail) { return((BLOCK_BUFFER_SIZE-1)-(block_buffer_head-block_buffer_tail)); }
return((block_buffer_tail-block_buffer_head-1));
}
// Returns the number of active blocks are in the planner buffer.
// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h
uint8_t plan_get_block_buffer_count()
{
if (block_buffer_head >= block_buffer_tail) { return(block_buffer_head-block_buffer_tail); }
return(BLOCK_BUFFER_SIZE - (block_buffer_tail-block_buffer_head));
}
// Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail.
// Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped.
void plan_cycle_reinitialize()
{
// Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer.
st_update_plan_block_parameters();
block_buffer_planned = block_buffer_tail;
planner_recalculate();
}

View File

@ -0,0 +1,152 @@
/*
planner.h - buffers movement commands and manages the acceleration profile plan
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 planner_h
#define planner_h
// The number of linear motions that can be in the plan at any give time
#ifndef BLOCK_BUFFER_SIZE
#ifdef USE_LINE_NUMBERS
#define BLOCK_BUFFER_SIZE 15
#else
#define BLOCK_BUFFER_SIZE 16
#endif
#endif
// Returned status message from planner.
#define PLAN_OK true
#define PLAN_EMPTY_BLOCK false
// Define planner data condition flags. Used to denote running conditions of a block.
#define PL_COND_FLAG_RAPID_MOTION bit(0)
#define PL_COND_FLAG_SYSTEM_MOTION bit(1) // Single motion. Circumvents planner state. Used by home/park.
#define PL_COND_FLAG_NO_FEED_OVERRIDE bit(2) // Motion does not honor feed override.
#define PL_COND_FLAG_INVERSE_TIME bit(3) // Interprets feed rate value as inverse time when set.
#define PL_COND_FLAG_SPINDLE_CW bit(4)
#define PL_COND_FLAG_SPINDLE_CCW bit(5)
#define PL_COND_FLAG_COOLANT_FLOOD bit(6)
#define PL_COND_FLAG_COOLANT_MIST bit(7)
#define PL_COND_MOTION_MASK (PL_COND_FLAG_RAPID_MOTION|PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE)
#define PL_COND_ACCESSORY_MASK (PL_COND_FLAG_SPINDLE_CW|PL_COND_FLAG_SPINDLE_CCW|PL_COND_FLAG_COOLANT_FLOOD|PL_COND_FLAG_COOLANT_MIST)
// This struct stores a linear movement of a g-code block motion with its critical "nominal" values
// are as specified in the source g-code.
typedef struct {
// Fields used by the bresenham algorithm for tracing the line
// NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values.
uint32_t steps[N_AXIS]; // Step count along each axis
uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block.
uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
// Block condition data to ensure correct execution depending on states and overrides.
uint8_t condition; // Block bitflag variable defining block run conditions. Copied from pl_line_data.
#ifdef USE_LINE_NUMBERS
int32_t line_number; // Block line number for real-time reporting. Copied from pl_line_data.
#endif
// Fields used by the motion planner to manage acceleration. Some of these values may be updated
// by the stepper module during execution of special motion cases for replanning purposes.
float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2
float max_entry_speed_sqr; // Maximum allowable entry speed based on the minimum of junction limit and
// neighboring nominal speeds with overrides in (mm/min)^2
float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2). Does not change.
float millimeters; // The remaining distance for this block to be executed in (mm).
// NOTE: This value may be altered by stepper algorithm during execution.
// Stored rate limiting data used by planner when changes occur.
float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2
float rapid_rate; // Axis-limit adjusted maximum rate for this block direction in (mm/min)
float programmed_rate; // Programmed rate of this block (mm/min).
//#ifdef VARIABLE_SPINDLE
// Stored spindle speed data used by spindle overrides and resuming methods.
float spindle_speed; // Block spindle speed. Copied from pl_line_data.
//#endif
} plan_block_t;
// Planner data prototype. Must be used when passing new motions to the planner.
typedef struct {
float feed_rate; // Desired feed rate for line motion. Value is ignored, if rapid motion.
float spindle_speed; // Desired spindle speed through line motion.
uint8_t condition; // Bitflag variable to indicate planner conditions. See defines above.
#ifdef USE_LINE_NUMBERS
int32_t line_number; // Desired line number to report when executing.
#endif
} plan_line_data_t;
// Initialize and reset the motion plan subsystem
void plan_reset(); // Reset all
void plan_reset_buffer(); // Reset buffer only.
// Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position
// in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
// rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data);
// Called when the current block is no longer needed. Discards the block and makes the memory
// availible for new blocks.
void plan_discard_current_block();
// Gets the planner block for the special system motion cases. (Parking/Homing)
plan_block_t *plan_get_system_motion_block();
// Gets the current block. Returns NULL if buffer empty
plan_block_t *plan_get_current_block();
// Called periodically by step segment buffer. Mostly used internally by planner.
uint8_t plan_next_block_index(uint8_t block_index);
// Called by step segment buffer when computing executing block velocity profile.
float plan_get_exec_block_exit_speed_sqr();
// Called by main program during planner calculations and step segment buffer during initialization.
float plan_compute_profile_nominal_speed(plan_block_t *block);
// Re-calculates buffered motions profile parameters upon a motion-based override change.
void plan_update_velocity_profile_parameters();
// Reset the planner position vector (in steps)
void plan_sync_position();
// Reinitialize plan with a partially completed block
void plan_cycle_reinitialize();
// Returns the number of available blocks are in the planner buffer.
uint8_t plan_get_block_buffer_available();
// Returns the number of active blocks are in the planner buffer.
// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h
uint8_t plan_get_block_buffer_count();
// Returns the status of the block ring buffer. True, if buffer is full.
uint8_t plan_check_full_buffer();
void plan_get_planner_mpos(float *target);
#endif

View File

@ -0,0 +1,299 @@
/*
kinematics_polar_coaster.cpp - Implements simple inverse kinematics for Grbl_ESP32
Part of Grbl_ESP32
Copyright (c) 2019 Barton Dring @buildlog
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/>.
Inverse kinematics determine the joint parameters required to get to a position
in 3D space. Grbl will still work as 3 axes of steps, but these steps could
represent angles, etc instead of linear units.
Unless forward kinematics are applied to the reporting, Grbl will report raw joint
values instead of the normal Cartesian positions
How it works...
If you tell it to go to X10 Y10 Z10 in Cartesian space, for example, the equations
will convert those values to the required joint values. In the case of a polar machine, X represents the radius,
Y represents the polar degrees and Z would be unchanged.
In most cases, a straight line in Cartesian space could cause a curve in the new system.
To fix this, the line is broken into very small segments and each segment is converted
to the new space. While each segment is also distorted, the amount is so small it cannot be seen.
This segmentation is how normal Grbl draws arcs.
Feed Rate
Feed rate is given in steps/time. Due to the new coordinate units and non linearity issues, the
feed rate may need to be adjusted. The ratio of the step distances in the original coordinate system
determined and applied to the feed rate.
TODO:
Add y offset, for completeness
Add ZERO_NON_HOMED_AXES option
*/
#include "grbl.h"
#ifdef CPU_MAP_POLAR_COASTER
#ifdef USE_KINEMATICS
static float last_angle = 0;
static float last_radius = 0;
// this get called before homing
// return false to complete normal home
// return true to exit normal homing
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
}
void kinematics_post_homing() {
// sync the X axis (do not need sync but make it for the fail safe)
last_radius = sys_position[X_AXIS];
// reset the internal angle value
last_angle = 0;
}
/*
Apply inverse kinematics for a polar system
float target: The desired target location in machine space
plan_line_data_t *pl_data: Plan information like feed rate, etc
float *position: The previous "from" location of the move
Note: It is assumed only the radius axis (X) is homed and only X and Z have offsets
*/
void inverse_kinematics(float *target, plan_line_data_t *pl_data, float *position)
{
//static float last_angle = 0;
//static float last_radius = 0;
float dx, dy, dz; // distances in each cartesian axis
float p_dx, p_dy, p_dz; // distances in each polar axis
float dist, polar_dist; // the distances in both systems...used to determine feed rate
uint32_t segment_count; // number of segments the move will be broken in to.
float seg_target[N_AXIS]; // The target of the current segment
float polar[N_AXIS]; // target location in polar coordinates
float x_offset = gc_state.coord_system[X_AXIS]+gc_state.coord_offset[X_AXIS]; // offset from machine coordinate system
float z_offset = gc_state.coord_system[Z_AXIS]+gc_state.coord_offset[Z_AXIS]; // offset from machine coordinate system
//grbl_sendf(CLIENT_SERIAL, "Position: %4.2f %4.2f %4.2f \r\n", position[X_AXIS] - x_offset, position[Y_AXIS], position[Z_AXIS]);
//grbl_sendf(CLIENT_SERIAL, "Target: %4.2f %4.2f %4.2f \r\n", target[X_AXIS] - x_offset, target[Y_AXIS], target[Z_AXIS]);
// calculate cartesian move distance for each axis
dx = target[X_AXIS] - position[X_AXIS];
dy = target[Y_AXIS] - position[Y_AXIS];
dz = target[Z_AXIS] - position[Z_AXIS];
// calculate the total X,Y axis move distance
// Z axis is the same in both coord systems, so it is ignored
dist = sqrt( (dx * dx) + (dy * dy) + (dz * dz));
if (pl_data->condition & PL_COND_FLAG_RAPID_MOTION) {
segment_count = 1; // rapid G0 motion is not used to draw, so skip the segmentation
} else {
segment_count = ceil(dist / SEGMENT_LENGTH); // determine the number of segments we need ... round up so there is at least 1
}
dist /= segment_count; // segment distance
for(uint32_t segment = 1; segment <= segment_count; segment++) {
// determine this segment's target
seg_target[X_AXIS] = position[X_AXIS] + (dx / float(segment_count) * segment) - x_offset;
seg_target[Y_AXIS] = position[Y_AXIS] + (dy / float(segment_count) * segment);
seg_target[Z_AXIS] = position[Z_AXIS] + (dz / float(segment_count) * segment) - z_offset;
calc_polar(seg_target, polar, last_angle);
// begin determining new feed rate
// calculate move distance for each axis
p_dx = polar[RADIUS_AXIS] - last_radius;
p_dy = polar[POLAR_AXIS] - last_angle;
p_dz = dz;
polar_dist = sqrt( (p_dx * p_dx) + (p_dy * p_dy) +(p_dz * p_dz)); // calculate the total move distance
float polar_rate_multiply = 1.0; // fail safe rate
if (polar_dist == 0 || dist == 0) { // prevent 0 feed rate and division by 0
polar_rate_multiply = 1.0; // default to same feed rate
} else {
// calc a feed rate multiplier
polar_rate_multiply = polar_dist / dist;
if (polar_rate_multiply < 0.5) { // prevent much slower speed
polar_rate_multiply = 0.5;
}
}
pl_data->feed_rate *= polar_rate_multiply; // apply the distance ratio between coord systems
// end determining new feed rate
polar[RADIUS_AXIS] += x_offset;
polar[Z_AXIS] += z_offset;
last_radius = polar[RADIUS_AXIS];
last_angle = polar[POLAR_AXIS];
mc_line(polar, pl_data);
}
// TO DO don't need a feedrate for rapids
}
/*
Forward kinematics converts position back to the original cartesian system. It is
typically used for reporting
For example, on a polar machine, you tell it to go to a place like X10Y10. It
converts to a radius and angle using inverse kinematics. The machine posiiton is now
in those units X14.14 (radius) and Y45 (degrees). If you want to report those units as
X10,Y10, you would use forward kinematics
position = the current machine position
converted = position with forward kinematics applied.
*/
void forward_kinematics(float *position)
{
float original_position[N_AXIS]; // temporary storage of original
float print_position[N_AXIS];
int32_t current_position[N_AXIS]; // Copy current state of the system position variable
memcpy(current_position,sys_position,sizeof(sys_position));
system_convert_array_steps_to_mpos(print_position,current_position);
original_position[X_AXIS] = print_position[X_AXIS] - gc_state.coord_system[X_AXIS]+gc_state.coord_offset[X_AXIS];
original_position[Y_AXIS] = print_position[Y_AXIS] - gc_state.coord_system[Y_AXIS]+gc_state.coord_offset[Y_AXIS];
original_position[Z_AXIS] = print_position[Z_AXIS] - gc_state.coord_system[Z_AXIS]+gc_state.coord_offset[Z_AXIS];
position[X_AXIS] = cos(radians(original_position[Y_AXIS])) * original_position[X_AXIS] * -1;
position[Y_AXIS] = sin(radians(original_position[Y_AXIS])) * original_position[X_AXIS];
position[Z_AXIS] = original_position[Z_AXIS]; // unchanged
}
// helper functions
/*******************************************
* Calculate polar values from Cartesian values
* float target_xyz: An array of target axis positions in Cartesian (xyz) space
* float polar: An array to return the polar values
* float last_angle: The polar angle of the "from" point.
*
* Angle calculated is 0 to 360, but you don't want a line to go from 350 to 10. This would
* be a long line backwards. You want it to go from 350 to 370. The same is true going the other way.
*
* This means the angle could accumulate to very high positive or negative values over the coarse of
* a long job.
*
*/
void calc_polar(float *target_xyz, float *polar, float last_angle)
{
float delta_ang; // the difference from the last and next angle
polar[RADIUS_AXIS] = hypot_f(target_xyz[X_AXIS], target_xyz[Y_AXIS]);
if (polar[RADIUS_AXIS] == 0) {
polar[POLAR_AXIS] = last_angle; // don't care about angle at center
}
else {
polar[POLAR_AXIS] = atan2(target_xyz[Y_AXIS], target_xyz[X_AXIS]) * 180.0 / M_PI;
// no negative angles...we want the absolute angle not -90, use 270
polar[POLAR_AXIS] = abs_angle(polar[POLAR_AXIS]);
}
polar[Z_AXIS] = target_xyz[Z_AXIS]; // Z is unchanged
delta_ang = polar[POLAR_AXIS] - abs_angle(last_angle);
// if the delta is above 180 degrees it means we are crossing the 0 degree line
if ( fabs(delta_ang) <= 180.0) {
polar[POLAR_AXIS] = last_angle + delta_ang;
} else {
if (delta_ang > 0.0) { // crossing zero counter clockwise
polar[POLAR_AXIS] = last_angle - (360.0 - delta_ang);
} else {
polar[POLAR_AXIS] = last_angle + delta_ang + 360.0;
}
}
}
// Return a 0-360 angle ... fix above 360 and below zero
float abs_angle(float ang) {
ang = fmod(ang, 360.0); // 0-360 or 0 to -360
if (ang < 0.0) {
ang = 360.0 + ang;
}
return ang;
}
#endif
// Polar coaster has macro buttons, this handles those button pushes.
void user_defined_macro(uint8_t index)
{
//grbl_sendf(CLIENT_SERIAL, "[MSG: Macro #%d]\r\n", index);
switch (index) {
#ifdef MACRO_BUTTON_0_PIN
case CONTROL_PIN_INDEX_MACRO_0:
inputBuffer.push("$H\r"); // home machine
break;
#endif
#ifdef MACRO_BUTTON_1_PIN
case CONTROL_PIN_INDEX_MACRO_1:
inputBuffer.push("[ESP220]/1.nc\r"); // run SD card file 1.nc
break;
#endif
#ifdef MACRO_BUTTON_2_PIN
case CONTROL_PIN_INDEX_MACRO_2:
inputBuffer.push("[ESP220]/2.nc\r"); // run SD card file 2.nc
break;
#endif
#ifdef MACRO_BUTTON_3_PIN
case CONTROL_PIN_INDEX_MACRO_3:
break;
#endif
default:
break;
}
}
// handle the M30 command
void user_m30() {
inputBuffer.push("$H\r");
}
#endif

View File

@ -0,0 +1,158 @@
/*
kinematics_polar_coaster.h - Implements simple kinematics for Grbl_ESP32
Part of Grbl_ESP32
Copyright (c) 2019 Barton Dring @buildlog
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 RADIUS_AXIS 0
#define POLAR_AXIS 1
#define SEGMENT_LENGTH 0.5 // segment length in mm
#define USE_KINEMATICS
#define FWD_KINEMATICS_REPORTING // report in cartesian
#define USE_M30
// ============= Begin CPU MAP ================
#define CPU_MAP_NAME "CPU_MAP_POLAR_COASTER"
#define USE_RMT_STEPS
#define X_STEP_PIN GPIO_NUM_15
#define X_RMT_CHANNEL 0
#define Y_STEP_PIN GPIO_NUM_2
#define Y_RMT_CHANNEL 1
#define X_DIRECTION_PIN GPIO_NUM_25
#define Y_DIRECTION_PIN GPIO_NUM_26
#define STEPPERS_DISABLE_PIN GPIO_NUM_17
#ifndef USE_SERVO_AXES // maybe set in config.h
#define USE_SERVO_AXES
#endif
#define SERVO_Z_PIN GPIO_NUM_16
#define SERVO_Z_CHANNEL_NUM 5
#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 LIMIT_MASK B1
#ifdef IGNORE_CONTROL_PINS // maybe set in config.h
#undef IGNORE_CONTROL_PINS
#endif
#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
#define MACRO_BUTTON_0_PIN GPIO_NUM_13
#define MACRO_BUTTON_1_PIN GPIO_NUM_12
#define MACRO_BUTTON_2_PIN GPIO_NUM_14
// redefine some stuff from config.h
#ifdef HOMING_CYCLE_0
#undef HOMING_CYCLE_0
#endif
#define HOMING_CYCLE_0 (1<<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
// ============= End CPU MAP ==================
// ============= Begin Default Settings ================
#define DEFAULT_STEP_PULSE_MICROSECONDS 3
#define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // stay on
#define DEFAULT_STEPPING_INVERT_MASK 0 // uint8_t
#define DEFAULT_DIRECTION_INVERT_MASK 2 // 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 2 // MPos enabled
#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 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_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 200.0
#define DEFAULT_Y_STEPS_PER_MM 71.111
#define DEFAULT_Z_STEPS_PER_MM 100.0 // This is percent in servo mode
#define DEFAULT_X_MAX_RATE 5000.0 // mm/min
#define DEFAULT_Y_MAX_RATE 15000.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_Y_ACCELERATION (200.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 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_Z_MAX_TRAVEL 100.0 // This is percent in servo mode
// ============= End Default Settings ==================
#ifndef kinematics_h
#define kinematics_h
#include "grbl.h"
bool kinematics_pre_homing(uint8_t cycle_mask);
void kinematics_post_homing();
void inverse_kinematics(float *target, plan_line_data_t *pl_data, float *position);
void calc_polar(float *target_xyz, float *polar, float last_angle);
float abs_angle(float ang);
void user_defined_macro(uint8_t index);
void forward_kinematics(float *position);
void user_m30();
#endif

View File

@ -0,0 +1,149 @@
/*
print.c - Functions for formatting output strings
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
// void printIntegerInBase(unsigned long n, unsigned long base)
// {
// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
// unsigned long i = 0;
//
// if (n == 0) {
// serial_write('0');
// return;
// }
//
// while (n > 0) {
// buf[i++] = n % base;
// n /= base;
// }
//
// for (; i > 0; i--)
// serial_write(buf[i - 1] < 10 ?
// '0' + buf[i - 1] :
// 'A' + buf[i - 1] - 10);
// }
// Prints an uint8 variable in base 10.
void print_uint8_base10(uint8_t n)
{
uint8_t digit_a = 0;
uint8_t digit_b = 0;
if (n >= 100) { // 100-255
digit_a = '0' + n % 10;
n /= 10;
}
if (n >= 10) { // 10-99
digit_b = '0' + n % 10;
n /= 10;
}
serial_write('0' + n);
if (digit_b) { serial_write(digit_b); }
if (digit_a) { serial_write(digit_a); }
}
// Prints an uint8 variable in base 2 with desired number of desired digits.
void print_uint8_base2_ndigit(uint8_t n, uint8_t digits) {
unsigned char buf[digits];
uint8_t i = 0;
for (; i < digits; i++) {
buf[i] = n % 2 ;
n /= 2;
}
for (; i > 0; i--)
Serial.print('0' + buf[i - 1]);
}
void print_uint32_base10(uint32_t n)
{
if (n == 0) {
Serial.print('0');
return;
}
unsigned char buf[10];
uint8_t i = 0;
while (n > 0) {
buf[i++] = n % 10;
n /= 10;
}
for (; i > 0; i--)
Serial.print('0' + buf[i-1]);
}
void printInteger(long n)
{
if (n < 0) {
Serial.print('-');
print_uint32_base10(-n);
} else {
print_uint32_base10(n);
}
}
// Convert float to string by immediately converting to a long integer, which contains
// more digits than a float. Number of decimal places, which are tracked by a counter,
// may be set by the user. The integer is then efficiently converted to a string.
// NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up
// techniques are actually just slightly slower. Found this out the hard way.
void printFloat(float n, uint8_t decimal_places)
{
Serial.print(n, decimal_places);
}
// Floating value printing handlers for special variables types used in Grbl and are defined
// in the config.h.
// - CoordValue: Handles all position or coordinate values in inches or mm reporting.
// - RateValue: Handles feed rate and current velocity in inches or mm reporting.
void printFloat_CoordValue(float n) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
printFloat(n*INCH_PER_MM,N_DECIMAL_COORDVALUE_INCH);
} else {
printFloat(n,N_DECIMAL_COORDVALUE_MM);
}
}
// Debug tool to print free memory in bytes at the called point.
// NOTE: Keep commented unless using. Part of this function always gets compiled in.
// void printFreeMemory()
// {
// extern int __heap_start, *__brkval;
// uint16_t free; // Up to 64k values.
// free = (int) &free - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
// printInteger((int32_t)free);
// printString(" ");
// }

View File

@ -0,0 +1,54 @@
/*
print.h - Functions for formatting output strings
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 print_h
#define print_h
void printString(const char *s);
void printPgmString(const char *s);
void printInteger(long n);
void print_uint32_base10(uint32_t n);
// Prints an uint8 variable in base 10.
void print_uint8_base10(uint8_t n);
// Prints an uint8 variable in base 2 with desired number of desired digits.
void print_uint8_base2_ndigit(uint8_t n, uint8_t digits);
void printFloat(float n, uint8_t decimal_places);
// Floating value printing handlers for special variables types used in Grbl.
// - CoordValue: Handles all position or coordinate values in inches or mm reporting.
// - RateValue: Handles feed rate and current velocity in inches or mm reporting.
void printFloat_CoordValue(float n);
void printFloat_RateValue(float n);
// Debug tool to print free memory in bytes at the called point. Not used otherwise.
void printFreeMemory();
#endif

View File

@ -0,0 +1,78 @@
/*
probe.c - code pertaining to probing methods
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
// Inverts the probe pin state depending on user settings and probing cycle mode.
uint8_t probe_invert_mask;
// Probe pin initialization routine.
void probe_init()
{
#ifdef PROBE_PIN
#ifdef DISABLE_PROBE_PIN_PULL_UP
pinMode(PROBE_PIN, INPUT);
#else
pinMode(PROBE_PIN, INPUT_PULLUP); // Enable internal pull-up resistors. Normal high operation.
#endif
probe_configure_invert_mask(false); // Initialize invert mask.
#endif
}
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
// appropriately set the pin logic according to setting for normal-high/normal-low operation
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
void probe_configure_invert_mask(uint8_t is_probe_away)
{
probe_invert_mask = 0; // Initialize as zero.
if (bit_isfalse(settings.flags,BITFLAG_INVERT_PROBE_PIN)) { probe_invert_mask ^= PROBE_MASK; }
if (is_probe_away) { probe_invert_mask ^= PROBE_MASK; }
}
// Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor.
uint8_t probe_get_state()
{
#ifdef PROBE_PIN
return((digitalRead(PROBE_PIN)) ^ probe_invert_mask);
#else
return false;
#endif
}
// Monitors probe pin state and records the system position when detected. Called by the
// stepper ISR per ISR tick.
// NOTE: This function must be extremely efficient as to not bog down the stepper ISR.
void probe_state_monitor()
{
if (probe_get_state()) {
sys_probe_state = PROBE_OFF;
memcpy(sys_probe_position, sys_position, sizeof(sys_position));
bit_true(sys_rt_exec_state, EXEC_MOTION_CANCEL);
}
}

View File

@ -0,0 +1,46 @@
/*
probe.h - code pertaining to probing methods
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 probe_h
#define probe_h
// Values that define the probing state machine.
#define PROBE_OFF 0 // Probing disabled or not in use. (Must be zero.)
#define PROBE_ACTIVE 1 // Actively watching the input pin.
// Probe pin initialization routine.
void probe_init();
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
// appropriately set the pin logic according to setting for normal-high/normal-low operation
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
void probe_configure_invert_mask(uint8_t is_probe_away);
// Returns probe pin state. Triggered = true. Called by gcode parser and probe state monitor.
uint8_t probe_get_state();
// Monitors probe pin state and records the system position when detected. Called by the
// stepper ISR per ISR tick.
void probe_state_monitor();
#endif

View File

@ -0,0 +1,849 @@
/*
protocol.c - controls Grbl execution protocol and procedures
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "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();
/*
GRBL PRIMARY LOOP:
*/
void protocol_main_loop()
{
//uint8_t client = CLIENT_SERIAL; // default client
// Perform some machine checks to make sure everything is good to go.
#ifdef CHECK_LIMITS_AT_INIT
if (bit_istrue(settings.flags, BITFLAG_HARD_LIMIT_ENABLE)) {
if (limits_get_state()) {
sys.state = STATE_ALARM; // Ensure alarm state is active.
report_feedback_message(MESSAGE_CHECK_LIMITS);
}
}
#endif
// Check for and report alarm state after a reset, error, or an initial power up.
// NOTE: Sleep mode disables the stepper drivers and position can't be guaranteed.
// Re-initialize the sleep state as an ALARM mode to ensure user homes or acknowledges.
if (sys.state & (STATE_ALARM | STATE_SLEEP)) {
report_feedback_message(MESSAGE_ALARM_LOCK);
sys.state = STATE_ALARM; // Ensure alarm state is set.
} else {
// Check if the safety door is open.
sys.state = STATE_IDLE;
if (system_check_safety_door_ajar()) {
bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR);
protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state.
}
// All systems go!
system_execute_startup(line); // Execute startup script.
}
// ---------------------------------------------------------------------------------
// 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.
// ---------------------------------------------------------------------------------
uint8_t line_flags = 0;
uint8_t char_counter = 0;
uint8_t comment_char_counter = 0;
uint8_t c;
for (;;) {
// serialCheck(); // un comment this if you do this here rather than in a separate task
#ifdef ENABLE_SD_CARD
if (SD_ready_next) {
char fileLine[255];
if (readFileLine(fileLine)) {
SD_ready_next = false;
report_status_message(gc_execute_line(fileLine, SD_client), SD_client);
}
else {
char temp[50];
sd_get_current_filename(temp);
grbl_notifyf("SD print done", "%s print is successful", temp);
closeFile(); // close file and clear SD ready/running flags
}
}
#endif
// Process one line of incoming serial data, as the data becomes available. Performs an
// initial filtering by removing spaces and comments and capitalizing all letters.
uint8_t client = CLIENT_SERIAL;
for (client = 0; client <= CLIENT_COUNT; client++)
{
while((c = serial_read(client)) != SERIAL_NO_DATA) {
if ((c == '\n') || (c == '\r')) { // End of line reached
protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to calling function upon system abort
line[char_counter] = 0; // Set string termination character.
#ifdef REPORT_ECHO_LINE_RECEIVED
report_echo_line_received(line, client);
#endif
// Direct and execute one line of formatted input, and report status of execution.
if (line_flags & LINE_FLAG_OVERFLOW) {
// Report line overflow error.
report_status_message(STATUS_OVERFLOW, client);
} else if (line[0] == 0) {
// Empty or comment line. For syncing purposes.
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_sendf(client, "[MSG: Unknow Command...%s]\r\n", 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
} // for clients
// If there are no more characters in the serial read buffer to be processed and executed,
// this indicates that g-code streaming has either filled the planner buffer or has
// completed. In either case, auto-cycle start, if enabled, any queued moves.
protocol_auto_cycle_start();
protocol_execute_realtime(); // Runtime command check point.
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.
if (stepper_idle)
{
if (esp_timer_get_time() > stepper_idle_counter)
{
set_stepper_disable(true);
}
}
}
return; /* Never reached */
}
// Block until all buffered steps are executed or in a cycle state. Works with feed hold
// during a synchronize call, if it should happen. Also, waits for clean cycle end.
void protocol_buffer_synchronize()
{
// If system is queued, ensure cycle resumes if the auto start flag is present.
protocol_auto_cycle_start();
do {
protocol_execute_realtime(); // Check and execute run-time commands
if (sys.abort) { return; } // Check for system abort
} while (plan_get_current_block() || (sys.state == STATE_CYCLE));
}
// Auto-cycle start triggers when there is a motion ready to execute and if the main program is not
// actively parsing commands.
// NOTE: This function is called from the main loop, buffer sync, and mc_line() only and executes
// when one of these conditions exist respectively: There are no more blocks sent (i.e. streaming
// is finished, single commands), a command that needs to wait for the motions in the buffer to
// execute calls a buffer sync, or the planner buffer is full and ready to go.
void protocol_auto_cycle_start()
{
if (plan_get_current_block() != NULL) { // Check if there are any blocks in the buffer.
system_set_exec_state_flag(EXEC_CYCLE_START); // If so, execute them!
}
}
// This function is the general interface to Grbl's real-time command execution system. It is called
// from various check points in the main program, primarily where there may be a while loop waiting
// for a buffer to clear space or any point where the execution time from the last check point may
// be more than a fraction of a second. This is a way to execute realtime commands asynchronously
// (aka multitasking) with grbl's g-code parsing and planning functions. This function also serves
// as an interface for the interrupts to set the system realtime flags, where only the main program
// handles them, removing the need to define more computationally-expensive volatile variables. This
// also provides a controlled way to execute certain tasks without having two or more instances of
// the same task, such as the planner recalculating the buffer upon a feedhold or overrides.
// NOTE: The sys_rt_exec_state variable flags are set by any process, step or serial interrupts, pinouts,
// limit switches, or the main program.
void protocol_execute_realtime()
{
protocol_exec_rt_system();
if (sys.suspend) { protocol_exec_rt_suspend(); }
}
// Executes run-time commands, when required. This function primarily operates as Grbl's state
// machine and controls the various real-time features Grbl has to offer.
// NOTE: Do not alter this unless you know exactly what you are doing!
void protocol_exec_rt_system()
{
uint8_t rt_exec; // Temp variable to avoid calling volatile multiple times.
rt_exec = sys_rt_exec_alarm; // Copy volatile sys_rt_exec_alarm.
if (rt_exec) { // Enter only if any bit flag is true
// System alarm. Everything has shutdown by something that has gone severely wrong. Report
// the source of the error to the user. If critical, Grbl disables by entering an infinite
// loop until system reset/abort.
sys.state = STATE_ALARM; // Set system alarm state
report_alarm_message(rt_exec);
// Halt everything upon a critical event flag. Currently hard and soft limits flag this.
if ((rt_exec == EXEC_ALARM_HARD_LIMIT) || (rt_exec == EXEC_ALARM_SOFT_LIMIT)) {
report_feedback_message(MESSAGE_CRITICAL_EVENT);
system_clear_exec_state_flag(EXEC_RESET); // Disable any existing reset
do {
// Block everything, except reset and status reports, until user issues reset or power
// cycles. Hard limits typically occur while unattended or not paying attention. Gives
// the user and a GUI time to do what is needed before resetting, like killing the
// incoming stream. The same could be said about soft limits. While the position is not
// lost, continued streaming could cause a serious crash if by chance it gets executed.
} while (bit_isfalse(sys_rt_exec_state,EXEC_RESET));
}
system_clear_exec_alarm(); // Clear alarm
}
rt_exec = sys_rt_exec_state; // Copy volatile sys_rt_exec_state.
if (rt_exec) {
// Execute system abort.
if (rt_exec & EXEC_RESET) {
sys.abort = true; // Only place this is set true.
return; // Nothing else to do but exit.
}
// Execute and serial print status
if (rt_exec & EXEC_STATUS_REPORT) {
report_realtime_status(CLIENT_ALL);
system_clear_exec_state_flag(EXEC_STATUS_REPORT);
}
// NOTE: Once hold is initiated, the system immediately enters a suspend state to block all
// main program processes until either reset or resumed. This ensures a hold completes safely.
if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR | EXEC_SLEEP)) {
// State check for allowable states for hold methods.
if (!(sys.state & (STATE_ALARM | STATE_CHECK_MODE))) {
// If in CYCLE or JOG states, immediately initiate a motion HOLD.
if (sys.state & (STATE_CYCLE | STATE_JOG)) {
if (!(sys.suspend & (SUSPEND_MOTION_CANCEL | SUSPEND_JOG_CANCEL))) { // Block, if already holding.
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = STEP_CONTROL_EXECUTE_HOLD; // Initiate suspend state with active flag.
if (sys.state == STATE_JOG) { // Jog cancelled upon any hold event, except for sleeping.
if (!(rt_exec & EXEC_SLEEP)) { sys.suspend |= SUSPEND_JOG_CANCEL; }
}
}
}
// If IDLE, Grbl is not in motion. Simply indicate suspend state and hold is complete.
if (sys.state == STATE_IDLE) { sys.suspend = SUSPEND_HOLD_COMPLETE; }
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
// to halt and cancel the remainder of the motion.
if (rt_exec & EXEC_MOTION_CANCEL) {
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand
// to hold the CYCLE. Motion cancel is valid for a single planner block motion only, while jog cancel
// will handle and clear multiple planner block motions.
if (!(sys.state & STATE_JOG)) { sys.suspend |= SUSPEND_MOTION_CANCEL; } // NOTE: State is STATE_CYCLE.
}
// Execute a feed hold with deceleration, if required. Then, suspend system.
if (rt_exec & EXEC_FEED_HOLD) {
// Block SAFETY_DOOR, JOG, and SLEEP states from changing to HOLD state.
if (!(sys.state & (STATE_SAFETY_DOOR | STATE_JOG | STATE_SLEEP))) { sys.state = STATE_HOLD; }
}
// Execute a safety door stop with a feed hold and disable spindle/coolant.
// NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered
// devices (spindle/coolant), and blocks resuming until switch is re-engaged.
if (rt_exec & EXEC_SAFETY_DOOR) {
report_feedback_message(MESSAGE_SAFETY_DOOR_AJAR);
// If jogging, block safety door methods until jog cancel is complete. Just flag that it happened.
if (!(sys.suspend & SUSPEND_JOG_CANCEL)) {
// Check if the safety re-opened during a restore parking motion only. Ignore if
// already retracting, parked or in sleep state.
if (sys.state == STATE_SAFETY_DOOR) {
if (sys.suspend & SUSPEND_INITIATE_RESTORE) { // Actively restoring
#ifdef PARKING_ENABLE
// Set hold and reset appropriate control flags to restart parking sequence.
if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) {
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION);
sys.suspend &= ~(SUSPEND_HOLD_COMPLETE);
} // else NO_MOTION is active.
#endif
sys.suspend &= ~(SUSPEND_RETRACT_COMPLETE | SUSPEND_INITIATE_RESTORE | SUSPEND_RESTORE_COMPLETE);
sys.suspend |= SUSPEND_RESTART_RETRACT;
}
}
if (sys.state != STATE_SLEEP) { sys.state = STATE_SAFETY_DOOR; }
}
// NOTE: This flag doesn't change when the door closes, unlike sys.state. Ensures any parking motions
// are executed if the door switch closes and the state returns to HOLD.
sys.suspend |= SUSPEND_SAFETY_DOOR_AJAR;
}
}
if (rt_exec & EXEC_SLEEP) {
if (sys.state == STATE_ALARM) { sys.suspend |= (SUSPEND_RETRACT_COMPLETE|SUSPEND_HOLD_COMPLETE); }
sys.state = STATE_SLEEP;
}
system_clear_exec_state_flag((EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR | EXEC_SLEEP));
}
// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
if (rt_exec & EXEC_CYCLE_START) {
// Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
// Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) {
// Resume door state when parking motion has retracted and door has been closed.
if ((sys.state == STATE_SAFETY_DOOR) && !(sys.suspend & SUSPEND_SAFETY_DOOR_AJAR)) {
if (sys.suspend & SUSPEND_RESTORE_COMPLETE) {
sys.state = STATE_IDLE; // Set to IDLE to immediately resume the cycle.
} else if (sys.suspend & SUSPEND_RETRACT_COMPLETE) {
// Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
// NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
// the retraction execution is complete, which implies the initial feed hold is not active. To
// restore normal operation, the restore procedures must be initiated by the following flag. Once,
// they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
sys.suspend |= SUSPEND_INITIATE_RESTORE;
}
}
// Cycle start only when IDLE or when a hold is complete and ready to resume.
if ((sys.state == STATE_IDLE) || ((sys.state & STATE_HOLD) && (sys.suspend & SUSPEND_HOLD_COMPLETE))) {
if (sys.state == STATE_HOLD && sys.spindle_stop_ovr) {
sys.spindle_stop_ovr |= SPINDLE_STOP_OVR_RESTORE_CYCLE; // Set to restore in suspend routine and cycle start after.
} else {
// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
sys.step_control = STEP_CONTROL_NORMAL_OP; // Restore step control to normal operation
if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) {
sys.suspend = SUSPEND_DISABLE; // Break suspend state.
sys.state = STATE_CYCLE;
st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
st_wake_up();
} else { // Otherwise, do nothing. Set and resume IDLE state.
sys.suspend = SUSPEND_DISABLE; // Break suspend state.
sys.state = STATE_IDLE;
}
}
}
}
system_clear_exec_state_flag(EXEC_CYCLE_START);
}
if (rt_exec & EXEC_CYCLE_STOP) {
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
// realtime command execution in the main program, ensuring that the planner re-plans safely.
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
if ((sys.state & (STATE_HOLD|STATE_SAFETY_DOOR|STATE_SLEEP)) && !(sys.soft_limit) && !(sys.suspend & SUSPEND_JOG_CANCEL)) {
// Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
// has issued a resume command or reset.
plan_cycle_reinitialize();
if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { sys.suspend |= SUSPEND_HOLD_COMPLETE; }
bit_false(sys.step_control,(STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION));
} else {
// Motion complete. Includes CYCLE/JOG/HOMING states and jog cancel/motion cancel/soft limit events.
// NOTE: Motion and jog cancel both immediately return to idle after the hold completes.
if (sys.suspend & SUSPEND_JOG_CANCEL) { // For jog cancel, flush buffers and sync positions.
sys.step_control = STEP_CONTROL_NORMAL_OP;
plan_reset();
st_reset();
gc_sync_position();
plan_sync_position();
}
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) { // Only occurs when safety door opens during jog.
sys.suspend &= ~(SUSPEND_JOG_CANCEL);
sys.suspend |= SUSPEND_HOLD_COMPLETE;
sys.state = STATE_SAFETY_DOOR;
} else {
sys.suspend = SUSPEND_DISABLE;
sys.state = STATE_IDLE;
}
}
system_clear_exec_state_flag(EXEC_CYCLE_STOP);
}
}
// Execute overrides.
rt_exec = sys_rt_exec_motion_override; // Copy volatile sys_rt_exec_motion_override
if (rt_exec) {
system_clear_exec_motion_overrides(); // Clear all motion override flags.
uint8_t new_f_override = sys.f_override;
if (rt_exec & EXEC_FEED_OVR_RESET) { new_f_override = DEFAULT_FEED_OVERRIDE; }
if (rt_exec & EXEC_FEED_OVR_COARSE_PLUS) { new_f_override += FEED_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_COARSE_MINUS) { new_f_override -= FEED_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_FINE_PLUS) { new_f_override += FEED_OVERRIDE_FINE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_FINE_MINUS) { new_f_override -= FEED_OVERRIDE_FINE_INCREMENT; }
new_f_override = MIN(new_f_override,MAX_FEED_RATE_OVERRIDE);
new_f_override = MAX(new_f_override,MIN_FEED_RATE_OVERRIDE);
uint8_t new_r_override = sys.r_override;
if (rt_exec & EXEC_RAPID_OVR_RESET) { new_r_override = DEFAULT_RAPID_OVERRIDE; }
if (rt_exec & EXEC_RAPID_OVR_MEDIUM) { new_r_override = RAPID_OVERRIDE_MEDIUM; }
if (rt_exec & EXEC_RAPID_OVR_LOW) { new_r_override = RAPID_OVERRIDE_LOW; }
if ((new_f_override != sys.f_override) || (new_r_override != sys.r_override)) {
sys.f_override = new_f_override;
sys.r_override = new_r_override;
sys.report_ovr_counter = 0; // Set to report change immediately
plan_update_velocity_profile_parameters();
plan_cycle_reinitialize();
}
}
rt_exec = sys_rt_exec_accessory_override;
if (rt_exec) {
system_clear_exec_accessory_overrides(); // Clear all accessory override flags.
// NOTE: Unlike motion overrides, spindle overrides do not require a planner reinitialization.
uint8_t last_s_override = sys.spindle_speed_ovr;
if (rt_exec & EXEC_SPINDLE_OVR_RESET) { last_s_override = DEFAULT_SPINDLE_SPEED_OVERRIDE; }
if (rt_exec & EXEC_SPINDLE_OVR_COARSE_PLUS) { last_s_override += SPINDLE_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_COARSE_MINUS) { last_s_override -= SPINDLE_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_FINE_PLUS) { last_s_override += SPINDLE_OVERRIDE_FINE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_FINE_MINUS) { last_s_override -= SPINDLE_OVERRIDE_FINE_INCREMENT; }
last_s_override = MIN(last_s_override,MAX_SPINDLE_SPEED_OVERRIDE);
last_s_override = MAX(last_s_override,MIN_SPINDLE_SPEED_OVERRIDE);
if (last_s_override != sys.spindle_speed_ovr) {
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
sys.spindle_speed_ovr = last_s_override;
sys.report_ovr_counter = 0; // Set to report change immediately
}
if (rt_exec & EXEC_SPINDLE_OVR_STOP) {
// Spindle stop override allowed only while in HOLD state.
// NOTE: Report counters are set in spindle_set_state() when spindle stop is executed.
if (sys.state == STATE_HOLD) {
if (!(sys.spindle_stop_ovr)) { sys.spindle_stop_ovr = SPINDLE_STOP_OVR_INITIATE; }
else if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_ENABLED) { sys.spindle_stop_ovr |= SPINDLE_STOP_OVR_RESTORE; }
}
}
// NOTE: Since coolant state always performs a planner sync whenever it changes, the current
// run state can be determined by checking the parser state.
if (rt_exec & (EXEC_COOLANT_FLOOD_OVR_TOGGLE | EXEC_COOLANT_MIST_OVR_TOGGLE)) {
if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOLD))) {
uint8_t coolant_state = gc_state.modal.coolant;
#ifdef COOLANT_FLOOD_PIN
if (rt_exec & EXEC_COOLANT_FLOOD_OVR_TOGGLE)
{
if (coolant_state & COOLANT_FLOOD_ENABLE) {
bit_false(coolant_state,COOLANT_FLOOD_ENABLE);
}
else {
coolant_state |= COOLANT_FLOOD_ENABLE;
}
}
#endif
#ifdef COOLANT_MIST_PIN
if (rt_exec & EXEC_COOLANT_MIST_OVR_TOGGLE) {
if (coolant_state & COOLANT_MIST_ENABLE) {
bit_false(coolant_state,COOLANT_MIST_ENABLE);
}
else {
coolant_state |= COOLANT_MIST_ENABLE;
}
}
#endif
coolant_set_state(coolant_state); // Report counter set in coolant_set_state().
gc_state.modal.coolant = coolant_state;
}
}
}
#ifdef DEBUG
if (sys_rt_exec_debug) {
report_realtime_debug();
sys_rt_exec_debug = 0;
}
#endif
// Reload step segment buffer
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_SAFETY_DOOR | STATE_HOMING | STATE_SLEEP| STATE_JOG)) {
st_prep_buffer();
}
}
// Handles Grbl system suspend procedures, such as feed hold, safety door, and parking motion.
// The system will enter this loop, create local variables for suspend tasks, and return to
// whatever function that invoked the suspend, such that Grbl resumes normal operation.
// This function is written in a way to promote custom parking motions. Simply use this as a
// template
static void protocol_exec_rt_suspend()
{
#ifdef PARKING_ENABLE
// Declare and initialize parking local variables
float restore_target[N_AXIS];
float parking_target[N_AXIS];
float retract_waypoint = PARKING_PULLOUT_INCREMENT;
plan_line_data_t plan_data;
plan_line_data_t *pl_data = &plan_data;
memset(pl_data,0,sizeof(plan_line_data_t));
pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE);
#ifdef USE_LINE_NUMBERS
pl_data->line_number = PARKING_MOTION_LINE_NUMBER;
#endif
#endif
plan_block_t *block = plan_get_current_block();
uint8_t restore_condition;
#ifdef VARIABLE_SPINDLE
float restore_spindle_speed;
if (block == NULL) {
restore_condition = (gc_state.modal.spindle | gc_state.modal.coolant);
restore_spindle_speed = gc_state.spindle_speed;
} else {
restore_condition = block->condition;
restore_spindle_speed = block->spindle_speed;
}
#ifdef DISABLE_LASER_DURING_HOLD
if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP);
}
#endif
#else
if (block == NULL) { restore_condition = (gc_state.modal.spindle | gc_state.modal.coolant); }
else { restore_condition = block->condition; }
#endif
while (sys.suspend) {
if (sys.abort) { return; }
// Block until initial hold is complete and the machine has stopped motion.
if (sys.suspend & SUSPEND_HOLD_COMPLETE) {
// Parking manager. Handles de/re-energizing, switch state checks, and parking motions for
// the safety door and sleep states.
if (sys.state & (STATE_SAFETY_DOOR | STATE_SLEEP)) {
// Handles retraction motions and de-energizing.
if (bit_isfalse(sys.suspend,SUSPEND_RETRACT_COMPLETE)) {
// Ensure any prior spindle stop override is disabled at start of safety door routine.
sys.spindle_stop_ovr = SPINDLE_STOP_OVR_DISABLED;
#ifndef PARKING_ENABLE
spindle_set_state(SPINDLE_DISABLE,0.0); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
#else
// Get current position and store restore location and spindle retract waypoint.
system_convert_array_steps_to_mpos(parking_target,sys_position);
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
memcpy(restore_target,parking_target,sizeof(parking_target));
retract_waypoint += restore_target[PARKING_AXIS];
retract_waypoint = MIN(retract_waypoint,PARKING_TARGET);
}
// Execute slow pull-out parking retract motion. Parking requires homing enabled, the
// 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.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
if ((bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) &&
(parking_target[PARKING_AXIS] < PARKING_TARGET) &&
bit_isfalse(settings.flags,BITFLAG_LASER_MODE) &&
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
#else
if ((bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) &&
(parking_target[PARKING_AXIS] < PARKING_TARGET) &&
bit_isfalse(settings.flags,BITFLAG_LASER_MODE)) {
#endif
// Retract spindle by pullout distance. Ensure retraction motion moves away from
// the workpiece and waypoint motion doesn't exceed the parking target location.
if (parking_target[PARKING_AXIS] < retract_waypoint) {
parking_target[PARKING_AXIS] = retract_waypoint;
pl_data->feed_rate = PARKING_PULLOUT_RATE;
pl_data->condition |= (restore_condition & PL_COND_ACCESSORY_MASK); // Retain accessory state
pl_data->spindle_speed = restore_spindle_speed;
mc_parking_motion(parking_target, pl_data);
}
// NOTE: Clear accessory state after retract and after an aborted restore motion.
pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE);
pl_data->spindle_speed = 0.0;
spindle_set_state(SPINDLE_DISABLE,0.0); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
// Execute fast parking retract motion to parking target location.
if (parking_target[PARKING_AXIS] < PARKING_TARGET) {
parking_target[PARKING_AXIS] = PARKING_TARGET;
pl_data->feed_rate = PARKING_RATE;
mc_parking_motion(parking_target, pl_data);
}
} else {
// Parking motion not possible. Just disable the spindle and coolant.
// NOTE: Laser mode does not start a parking motion to ensure the laser stops immediately.
spindle_set_state(SPINDLE_DISABLE,0.0); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
}
#endif
sys.suspend &= ~(SUSPEND_RESTART_RETRACT);
sys.suspend |= SUSPEND_RETRACT_COMPLETE;
} else {
if (sys.state == STATE_SLEEP) {
report_feedback_message(MESSAGE_SLEEP_MODE);
// Spindle and coolant should already be stopped, but do it again just to be sure.
spindle_set_state(SPINDLE_DISABLE,0.0); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
st_go_idle(); // Disable steppers
while (!(sys.abort)) { protocol_exec_rt_system(); } // Do nothing until reset.
return; // Abort received. Return to re-initialize.
}
// Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to resume.
if (sys.state == STATE_SAFETY_DOOR) {
if (!(system_check_safety_door_ajar())) {
sys.suspend &= ~(SUSPEND_SAFETY_DOOR_AJAR); // Reset door ajar flag to denote ready to resume.
}
}
// Handles parking restore and safety door resume.
if (sys.suspend & SUSPEND_INITIATE_RESTORE) {
#ifdef PARKING_ENABLE
// 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.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
if (((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) &&
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
#else
if ((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) {
#endif
// Check to ensure the motion doesn't move below pull-out position.
if (parking_target[PARKING_AXIS] <= PARKING_TARGET) {
parking_target[PARKING_AXIS] = retract_waypoint;
pl_data->feed_rate = PARKING_RATE;
mc_parking_motion(parking_target, pl_data);
}
}
#endif
// Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle.
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
// 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_PWM);
} else {
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SYS_SUSPEND);
}
}
}
if (gc_state.modal.coolant != COOLANT_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
// NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this pin.
coolant_set_state((restore_condition & (PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_FLOOD)));
delay_sec(SAFETY_DOOR_COOLANT_DELAY, DELAY_MODE_SYS_SUSPEND);
}
}
#ifdef PARKING_ENABLE
// Execute slow plunge motion from pull-out position to resume position.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
if (((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) &&
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
#else
if ((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) {
#endif
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
// Regardless if the retract parking motion was a valid/safe motion or not, the
// restore parking motion should logically be valid, either by returning to the
// original position through valid machine space or by not moving at all.
pl_data->feed_rate = PARKING_PULLOUT_RATE;
pl_data->condition |= (restore_condition & PL_COND_ACCESSORY_MASK); // Restore accessory state
pl_data->spindle_speed = restore_spindle_speed;
mc_parking_motion(restore_target, pl_data);
}
}
#endif
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
sys.suspend |= SUSPEND_RESTORE_COMPLETE;
system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.
}
}
}
} else {
// Feed hold manager. Controls spindle stop override states.
// NOTE: Hold ensured as completed by condition check at the beginning of suspend routine.
if (sys.spindle_stop_ovr) {
// Handles beginning of spindle stop
if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_INITIATE) {
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
spindle_set_state(SPINDLE_DISABLE,0.0); // De-energize
sys.spindle_stop_ovr = SPINDLE_STOP_OVR_ENABLED; // Set stop override state to enabled, if de-energized.
} else {
sys.spindle_stop_ovr = SPINDLE_STOP_OVR_DISABLED; // Clear stop override state
}
// Handles restoring of spindle state
} else if (sys.spindle_stop_ovr & (SPINDLE_STOP_OVR_RESTORE | SPINDLE_STOP_OVR_RESTORE_CYCLE)) {
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
report_feedback_message(MESSAGE_SPINDLE_RESTORE);
if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
// 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_PWM);
} else {
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
}
}
if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_RESTORE_CYCLE) {
system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.
}
sys.spindle_stop_ovr = SPINDLE_STOP_OVR_DISABLED; // Clear stop override state
}
} else {
// Handles spindle state during hold. NOTE: Spindle speed overrides may be altered during hold state.
// NOTE: STEP_CONTROL_UPDATE_SPINDLE_PWM is automatically reset upon resume in step generator.
if (bit_istrue(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM)) {
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
bit_false(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
}
}
}
}
protocol_exec_rt_system();
}
}

View File

@ -0,0 +1,56 @@
/*
protocol.h - controls Grbl execution protocol and procedures
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 protocol_h
#define protocol_h
// Line buffer size from the serial input stream to be executed.
// NOTE: Not a problem except for extreme cases, but the line buffer size can be too small
// and g-code blocks can get truncated. Officially, the g-code standards support up to 256
// characters. In future versions, this will be increased, when we know how much extra
// memory space we can invest into here or we re-write the g-code parser not to have this
// buffer.
#ifndef LINE_BUFFER_SIZE
#define LINE_BUFFER_SIZE 80
#endif
// Starts Grbl main loop. It handles all incoming characters from the serial port and executes
// them as they complete. It is also responsible for finishing the initialization procedures.
void protocol_main_loop();
// Checks and executes a realtime command at various stop points in main program
void protocol_execute_realtime();
void protocol_exec_rt_system();
// Executes the auto cycle feature, if enabled.
void protocol_auto_cycle_start();
// Block until all buffered steps are executed
void protocol_buffer_synchronize();
// Executes the auto cycle feature, if enabled.
void protocol_auto_cycle_start();
#endif

View File

@ -0,0 +1,874 @@
/*
report.c - reporting and messaging methods
Part of Grbl
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
*/
/*
This file functions as the primary feedback interface for Grbl. Any outgoing data, such
as the protocol status messages, feedback messages, and status reports, are stored here.
For the most part, these functions primarily are called from protocol.c methods. If a
different style feedback is desired (i.e. JSON), then a user can change these following
methods to accommodate their needs.
ESP32 Notes:
Major rewrite to fix issues with BlueTooth. As described here there is a
when you try to send data a single byte at a time using SerialBT.write(...).
https://github.com/espressif/arduino-esp32/issues/1537
A solution is to send messages as a string using SerialBT.print(...). Use
a short delay after each send. Therefore this file needed to be rewritten
to work that way. AVR Grbl was written to be super efficient to give it
good performance. This is far less efficient, but the ESP32 can handle it.
Do not use this version of the file with AVR Grbl.
ESP32 discussion here ... https://github.com/bdring/Grbl_Esp32/issues/3
*/
#include "grbl.h"
#define DEFAULTBUFFERSIZE 64
// this is a generic send function that everything should use, so interfaces could be added (Bluetooth, etc)
void grbl_send(uint8_t client, const char *text)
{
if (client == CLIENT_INPUT) return;
#ifdef ENABLE_BLUETOOTH
if (SerialBT.hasClient() && ( client == CLIENT_BT || client == CLIENT_ALL ) )
{
SerialBT.print(text);
//delay(10); // possible fix for dropped characters
}
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_OUT)
if ( client == CLIENT_WEBUI || client == CLIENT_ALL )
Serial2Socket.write((const uint8_t*)text, strlen(text));
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_TELNET)
if ( client == CLIENT_TELNET || client == CLIENT_ALL ){
telnet_server.write((const uint8_t*)text, strlen(text));
}
#endif
if ( client == CLIENT_SERIAL || client == CLIENT_ALL )
Serial.print(text);
}
// This is a formating version of the grbl_send(CLIENT_ALL,...) function that work like printf
void grbl_sendf(uint8_t client, const char *format, ...)
{
if (client == CLIENT_INPUT) return;
char loc_buf[64];
char * temp = loc_buf;
va_list arg;
va_list copy;
va_start(arg, format);
va_copy(copy, arg);
size_t len = vsnprintf(NULL, 0, format, arg);
va_end(copy);
if(len >= sizeof(loc_buf)){
temp = new char[len+1];
if(temp == NULL) {
return;
}
}
len = vsnprintf(temp, len+1, format, arg);
grbl_send(client, temp);
va_end(arg);
if(len > 64){
delete[] temp;
}
}
//function to notify
void grbl_notify(const char *title, const char *msg){
#ifdef ENABLE_NOTIFICATIONS
notificationsservice.sendMSG(title, msg);
#endif
}
void grbl_notifyf(const char *title, const char *format, ...){
char loc_buf[64];
char * temp = loc_buf;
va_list arg;
va_list copy;
va_start(arg, format);
va_copy(copy, arg);
size_t len = vsnprintf(NULL, 0, format, arg);
va_end(copy);
if(len >= sizeof(loc_buf)){
temp = new char[len+1];
if(temp == NULL) {
return;
}
}
len = vsnprintf(temp, len+1, format, arg);
grbl_notify(title, temp);
va_end(arg);
if(len > 64){
delete[] temp;
}
}
// formats axis values into a string and returns that string in rpt
static void report_util_axis_values(float *axis_value, char *rpt) {
uint8_t idx;
char axisVal[10];
float unit_conv = 1.0; // unit conversion multiplier..default is mm
rpt[0] = '\0';
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES))
unit_conv = 1.0 / MM_PER_INCH;
for (idx=0; idx<N_AXIS; idx++) {
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES))
sprintf(axisVal, "%4.4f", axis_value[idx] * unit_conv); // Report inches to 4 decimals
else
sprintf(axisVal, "%4.3f", axis_value[idx] * unit_conv); // Report mm to 3 decimals
strcat(rpt, axisVal);
if (idx < (N_AXIS-1))
{
strcat(rpt, ",");
}
}
}
void get_state(char *foo)
{
// pad them to same length
switch (sys.state) {
case STATE_IDLE: strcpy(foo," Idle ");; break;
case STATE_CYCLE: strcpy(foo," Run "); break;
case STATE_HOLD: strcpy(foo," Hold "); break;
case STATE_HOMING: strcpy(foo," Home "); break;
case STATE_ALARM: strcpy(foo," Alarm"); break;
case STATE_CHECK_MODE: strcpy(foo," Check"); break;
case STATE_SAFETY_DOOR: strcpy(foo," Door "); break;
default:strcpy(foo," ? "); break;
}
}
// Handles the primary confirmation protocol response for streaming interfaces and human-feedback.
// For every incoming line, this method responds with an 'ok' for a successful command or an
// 'error:' to indicate some error event with the line or some critical system error during
// operation. Errors events can originate from the g-code parser, settings module, or asynchronously
// from a critical error, such as a triggered hard limit. Interface should always monitor for these
// responses.
void report_status_message(uint8_t status_code, uint8_t client)
{
switch(status_code) {
case STATUS_OK: // STATUS_OK
#ifdef ENABLE_SD_CARD
if (get_sd_state(false) == SDCARD_BUSY_PRINTING)
SD_ready_next = true; // flag so system_execute_line() will send the next line
else
grbl_send(client,"ok\r\n");
#else
grbl_send(client,"ok\r\n");
#endif
break;
default:
#ifdef ENABLE_SD_CARD
// do we need to stop a running SD job?
if (get_sd_state(false) == SDCARD_BUSY_PRINTING) {
if (status_code == STATUS_GCODE_UNSUPPORTED_COMMAND) {
grbl_sendf(client, "error:%d\r\n", status_code); // most senders seem to tolerate this error and keep on going
grbl_sendf(CLIENT_ALL, "error:%d in SD file at line %d\r\n", status_code, sd_get_current_line_number());
// don't close file
}
else {
grbl_notifyf("SD print error", "Error:%d during SD file at line: %d", status_code, sd_get_current_line_number());
grbl_sendf(CLIENT_ALL, "error:%d in SD file at line %d\r\n", status_code, sd_get_current_line_number());
closeFile();
}
return;
}
#endif
grbl_sendf(client, "error:%d\r\n", status_code);
}
}
// Prints alarm messages.
void report_alarm_message(uint8_t alarm_code)
{
grbl_sendf(CLIENT_ALL, "ALARM:%d\r\n", alarm_code); // OK to send to all clients
delay_ms(500); // Force delay to ensure message clears serial write buffer.
}
// Prints feedback messages. This serves as a centralized method to provide additional
// user feedback for things that are not of the status/alarm message protocol. These are
// messages such as setup warnings, switch toggling, and how to exit alarms.
// NOTE: For interfaces, messages are always placed within brackets. And if silent mode
// is installed, the message number codes are less than zero.
void report_feedback_message(uint8_t message_code) // OK to send to all clients
{
switch(message_code) {
case MESSAGE_CRITICAL_EVENT:
grbl_send(CLIENT_ALL,"[MSG:Reset to continue]\r\n"); break;
case MESSAGE_ALARM_LOCK:
grbl_send(CLIENT_ALL, "[MSG:'$H'|'$X' to unlock]\r\n"); break;
case MESSAGE_ALARM_UNLOCK:
grbl_send(CLIENT_ALL, "[MSG:Caution: Unlocked]\r\n"); break;
case MESSAGE_ENABLED:
grbl_send(CLIENT_ALL, "[MSG:Enabled]\r\n"); break;
case MESSAGE_DISABLED:
grbl_send(CLIENT_ALL, "[MSG:Disabled]\r\n"); break;
case MESSAGE_SAFETY_DOOR_AJAR:
grbl_send(CLIENT_ALL, "[MSG:Check Door]\r\n"); break;
case MESSAGE_CHECK_LIMITS:
grbl_send(CLIENT_ALL, "[MSG:Check Limits]\r\n"); break;
case MESSAGE_PROGRAM_END:
grbl_send(CLIENT_ALL, "[MSG:Pgm End]\r\n"); break;
case MESSAGE_RESTORE_DEFAULTS:
grbl_send(CLIENT_ALL, "[MSG:Restoring defaults]\r\n"); break;
case MESSAGE_SPINDLE_RESTORE:
grbl_send(CLIENT_ALL, "[MSG:Restoring spindle]\r\n"); break;
case MESSAGE_SLEEP_MODE:
grbl_send(CLIENT_ALL, "[MSG:Sleeping]\r\n"); break;
#ifdef ENABLE_SD_CARD
case MESSAGE_SD_FILE_QUIT:
grbl_notifyf("SD print canceled", "Reset during SD file at line: %d", sd_get_current_line_number());
grbl_sendf(CLIENT_ALL, "[MSG:Reset during SD file at line: %d]\r\n", sd_get_current_line_number()); break;
#endif
}
}
// Welcome message
void report_init_message(uint8_t client)
{
#ifdef CPU_MAP_NAME
grbl_send(client,"[MSG:Using cpu_map..." CPU_MAP_NAME "]\r\n");
#endif
grbl_send(client,"\r\nGrbl " GRBL_VERSION " ['$' for help]\r\n");
}
// Grbl help message
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 global settings print out.
// NOTE: The numbering scheme here must correlate to storing in settings.c
void report_grbl_settings(uint8_t client) {
// Print Grbl settings.
char setting[20];
char rpt[1000];
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);
#ifdef VARIABLE_SPINDLE
sprintf(setting, "$32=%d\r\n", bit_istrue(settings.flags,BITFLAG_LASER_MODE)); strcat(rpt, setting);
#else
strcat(rpt, "$32=0\r\n");
#endif
#ifdef SHOW_EXTENDED_SETTINGS
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);
}
#endif
// 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;
#ifdef SHOW_EXTENDED_SETTINGS
case 4: sprintf(setting, "$%d=%4.3f\r\n", val+idx, settings.current[idx]); strcat(rpt, setting); break;
case 5: sprintf(setting, "$%d=%4.3f\r\n", val+idx, settings.hold_current[idx]); strcat(rpt, setting); break;
case 6: sprintf(setting, "$%d=%d\r\n", val+idx, settings.microsteps[idx]); strcat(rpt, setting); break;
case 7: sprintf(setting, "$%d=%d\r\n", val+idx, settings.stallguard[idx]); strcat(rpt, setting); break;
#endif
}
}
val += AXIS_SETTINGS_INCREMENT;
}
grbl_send(client,rpt);
}
// 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).
// These values are retained until Grbl is power-cycled, whereby they will be re-zeroed.
void report_probe_parameters(uint8_t client)
{
// Report in terms of machine position.
float print_position[N_AXIS];
char probe_rpt[100]; // the probe report we are building here
char temp[60];
strcpy(probe_rpt, "[PRB:"); // initialize the string with the first characters
// get the machine position and put them into a string and append to the probe report
system_convert_array_steps_to_mpos(print_position,sys_probe_position);
report_util_axis_values(print_position, temp);
strcat(probe_rpt, temp);
// add the success indicator and add closing characters
sprintf(temp, ":%d]\r\n", sys.probe_succeeded);
strcat(probe_rpt, temp);
grbl_send(client, probe_rpt); // send the report
}
// Prints Grbl NGC parameters (coordinate offsets, probing)
void report_ngc_parameters(uint8_t client)
{
float coord_data[N_AXIS];
uint8_t coord_select;
char temp[60];
char ngc_rpt[500];
ngc_rpt[0] = '\0';
for (coord_select = 0; coord_select <= SETTING_INDEX_NCOORD; coord_select++) {
if (!(settings_read_coord_data(coord_select,coord_data))) {
report_status_message(STATUS_SETTING_READ_FAIL, CLIENT_SERIAL);
return;
}
strcat(ngc_rpt, "[G");
switch (coord_select) {
case 6: strcat(ngc_rpt, "28"); break;
case 7: strcat(ngc_rpt, "30"); break;
default:
sprintf(temp, "%d", coord_select+54);
strcat(ngc_rpt, temp);
break; // G54-G59
}
strcat(ngc_rpt, ":");
report_util_axis_values(coord_data, temp);
strcat(ngc_rpt, temp);
strcat(ngc_rpt, "]\r\n");
}
strcat(ngc_rpt, "[G92:"); // Print G92,G92.1 which are not persistent in memory
report_util_axis_values(gc_state.coord_offset, temp);
strcat(ngc_rpt, temp);
strcat(ngc_rpt, "]\r\n");
strcat(ngc_rpt, "[TLO:"); // Print tool length offset value
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
sprintf(temp, "%4.3f]\r\n", gc_state.tool_length_offset * INCH_PER_MM);
} else {
sprintf(temp, "%4.3f]\r\n", gc_state.tool_length_offset);
}
strcat(ngc_rpt, temp);
grbl_send(client, ngc_rpt);
report_probe_parameters(client);
}
// Print current gcode parser mode state
void report_gcode_modes(uint8_t client)
{
char temp[20];
char modes_rpt[75];
strcpy(modes_rpt, "[GC:G");
if (gc_state.modal.motion >= MOTION_MODE_PROBE_TOWARD) {
sprintf(temp, "38.%d", gc_state.modal.motion - (MOTION_MODE_PROBE_TOWARD-2));
} else {
sprintf(temp, "%d", gc_state.modal.motion);
}
strcat(modes_rpt, temp);
sprintf(temp, " G%d", gc_state.modal.coord_select+54);
strcat(modes_rpt, temp);
sprintf(temp, " G%d", gc_state.modal.plane_select+17);
strcat(modes_rpt, temp);
sprintf(temp, " G%d", 21-gc_state.modal.units);
strcat(modes_rpt, temp);
sprintf(temp, " G%d", gc_state.modal.distance+90);
strcat(modes_rpt, temp);
sprintf(temp, " G%d", 94-gc_state.modal.feed_rate);
strcat(modes_rpt, temp);
if (gc_state.modal.program_flow) {
//report_util_gcode_modes_M();
switch (gc_state.modal.program_flow) {
case PROGRAM_FLOW_PAUSED : strcat(modes_rpt, " M0"); //serial_write('0'); break;
// case PROGRAM_FLOW_OPTIONAL_STOP : serial_write('1'); break; // M1 is ignored and not supported.
case PROGRAM_FLOW_COMPLETED_M2 :
case PROGRAM_FLOW_COMPLETED_M30 :
sprintf(temp, " M%d", gc_state.modal.program_flow);
strcat(modes_rpt, temp);
break;
}
}
switch (gc_state.modal.spindle) {
case SPINDLE_ENABLE_CW : strcat(modes_rpt, " M3"); break;
case SPINDLE_ENABLE_CCW : strcat(modes_rpt, " M4"); break;
case SPINDLE_DISABLE : strcat(modes_rpt, " M5"); break;
}
//report_util_gcode_modes_M(); // optional M7 and M8 should have been dealt with by here
if (gc_state.modal.coolant) { // Note: Multiple coolant states may be active at the same time.
if (gc_state.modal.coolant & PL_COND_FLAG_COOLANT_MIST) { strcat(modes_rpt, " M7"); }
if (gc_state.modal.coolant & PL_COND_FLAG_COOLANT_FLOOD) { strcat(modes_rpt, " M8"); }
}
else {
strcat(modes_rpt, " M9");
}
sprintf(temp, " T%d", gc_state.tool);
strcat(modes_rpt, temp);
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
sprintf(temp, " F%.1f", gc_state.feed_rate);
} else {
sprintf(temp, " F%.0f", gc_state.feed_rate);
}
strcat(modes_rpt, temp);
#ifdef VARIABLE_SPINDLE
sprintf(temp, " S%4.3f", gc_state.spindle_speed);
strcat(modes_rpt, temp);
#endif
strcat(modes_rpt, "]\r\n");
grbl_send(client, modes_rpt);
}
// Prints specified startup line
void report_startup_line(uint8_t n, char *line, uint8_t client)
{
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)
{
grbl_sendf(client, ">%s:", line); // OK to send to all
report_status_message(status_code, client);
}
// Prints build info line
void report_build_info(char *line, uint8_t client)
{
char build_info[50];
strcpy(build_info, "[VER:" GRBL_VERSION "." GRBL_VERSION_BUILD ":");
strcat(build_info, line);
strcat(build_info, "]\r\n[OPT:");
#ifdef VARIABLE_SPINDLE
strcat(build_info,"V");
#endif
#ifdef USE_LINE_NUMBERS
strcat(build_info,"N");
#endif
#ifdef COOLANT_MIST_PIN
strcat(build_info,"M"); // TODO Need to deal with M8...it could be disabled
#endif
#ifdef COREXY
strcat(build_info,"C");
#endif
#ifdef PARKING_ENABLE
strcat(build_info,"P");
#endif
#if (defined(HOMING_FORCE_SET_ORIGIN) || defined(HOMING_FORCE_POSITIVE_SPACE))
strcat(build_info,"Z"); // homing MPOS bahavior is not the default behavior
#endif
#ifdef HOMING_SINGLE_AXIS_COMMANDS
strcat(build_info,"H");
#endif
#ifdef LIMITS_TWO_SWITCHES_ON_AXES
strcat(build_info,"L");
#endif
#ifdef ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES
strcat(build_info,"A");
#endif
#ifdef ENABLE_BLUETOOTH
strcat(build_info,"B");
#endif
#ifdef ENABLE_SD_CARD
strcat(build_info,"S");
#endif
#if defined (ENABLE_WIFI)
strcat(build_info,"W");
#endif
#ifndef ENABLE_RESTORE_EEPROM_WIPE_ALL // NOTE: Shown when disabled.
strcat(build_info,"*");
#endif
#ifndef ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS // NOTE: Shown when disabled.
strcat(build_info,"$");
#endif
#ifndef ENABLE_RESTORE_EEPROM_CLEAR_PARAMETERS // NOTE: Shown when disabled.
strcat(build_info,"#");
#endif
#ifndef ENABLE_BUILD_INFO_WRITE_COMMAND // NOTE: Shown when disabled.
strcat(build_info,"I");
#endif
#ifndef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE // NOTE: Shown when disabled.
strcat(build_info,"E");
#endif
#ifndef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE // NOTE: Shown when disabled.
strcat(build_info,"W");
#endif
// NOTE: Compiled values, like override increments/max/min values, may be added at some point later.
// These will likely have a comma delimiter to separate them.
strcat(build_info,"]\r\n");
grbl_send(client, build_info); // ok to send to all
#if defined (ENABLE_WIFI)
grbl_send(client, (char *)wifi_config.info());
#endif
#if defined (ENABLE_BLUETOOTH)
grbl_send(client, (char *)bt_config.info());
#endif
}
// 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.
void report_echo_line_received(char *line, uint8_t client)
{
grbl_sendf(client, "[echo: %s]\r\n", line);
}
// Prints real-time data. This function grabs a real-time snapshot of the stepper subprogram
// and the actual location of the CNC machine. Users may change the following function to their
// specific needs, but the desired real-time data report must be as short as possible. This is
// requires as it minimizes the computational overhead and allows grbl to keep running smoothly,
// especially during g-code programs with fast, short line segments and high frequency reports (5-20Hz).
void report_realtime_status(uint8_t client)
{
uint8_t idx;
int32_t current_position[N_AXIS]; // Copy current state of the system position variable
memcpy(current_position,sys_position,sizeof(sys_position));
float print_position[N_AXIS];
char status[200];
char temp[80];
system_convert_array_steps_to_mpos(print_position,current_position);
// Report current machine state and sub-states
strcpy(status, "<");
switch (sys.state) {
case STATE_IDLE: strcat(status, "Idle"); break;
case STATE_CYCLE: strcat(status, "Run"); break;
case STATE_HOLD:
if (!(sys.suspend & SUSPEND_JOG_CANCEL)) {
strcat(status, "Hold:");
if (sys.suspend & SUSPEND_HOLD_COMPLETE) { strcat(status, "0"); } // Ready to resume
else { strcat(status, "1"); } // Actively holding
break;
} // Continues to print jog state during jog cancel.
case STATE_JOG: strcat(status, "Jog"); break;
case STATE_HOMING: strcat(status, "Home"); break;
case STATE_ALARM: strcat(status, "Alarm"); break;
case STATE_CHECK_MODE: strcat(status, "Check"); break;
case STATE_SAFETY_DOOR:
strcat(status, "Door:");
if (sys.suspend & SUSPEND_INITIATE_RESTORE) {
strcat(status, "3"); // Restoring
} else {
if (sys.suspend & SUSPEND_RETRACT_COMPLETE) {
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
strcat(status, "1"); // Door ajar
} else {
strcat(status, "0");
} // Door closed and ready to resume
} else {
strcat(status, "2"); // Retracting
}
}
break;
case STATE_SLEEP: strcat(status, "Sleep"); break;
}
float wco[N_AXIS];
if (bit_isfalse(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE) ||
(sys.report_wco_counter == 0) ) {
for (idx=0; idx< N_AXIS; idx++) {
// Apply work coordinate offsets and tool length offset to current position.
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 (bit_isfalse(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE)) {
print_position[idx] -= wco[idx];
}
}
}
// Report machine position
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE)) {
strcat(status, "|MPos:");
} else {
#ifdef FWD_KINEMATICS_REPORTING
forward_kinematics(print_position);
#endif
strcat(status, "|WPos:");
}
report_util_axis_values(print_position, temp);
strcat(status, temp);
// Returns planner and serial read buffer states.
#ifdef REPORT_FIELD_BUFFER_STATE
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_BUFFER_STATE)) {
int bufsize = DEFAULTBUFFERSIZE;
#if defined (ENABLE_WIFI) && defined(ENABLE_TELNET)
if (client == CLIENT_TELNET){
bufsize = telnet_server.get_rx_buffer_available();
}
#endif //ENABLE_WIFI && ENABLE_TELNET
#if defined(ENABLE_BLUETOOTH)
if (client == CLIENT_BT){
//TODO FIXME
bufsize = 512 - SerialBT.available();
}
#endif //ENABLE_BLUETOOTH
if (client == CLIENT_SERIAL){
bufsize = serial_get_rx_buffer_available(CLIENT_SERIAL);
}
sprintf(temp, "|Bf:%d,%d", plan_get_block_buffer_available(), bufsize);
strcat(status, temp);
}
#endif
#ifdef USE_LINE_NUMBERS
#ifdef REPORT_FIELD_LINE_NUMBERS
// Report current line number
plan_block_t * cur_block = plan_get_current_block();
if (cur_block != NULL) {
uint32_t ln = cur_block->line_number;
if (ln > 0) {
sprintf(temp, "|Ln:%d", ln);
strcat(status, temp);
}
}
#endif
#endif
// Report realtime feed speed
#ifdef REPORT_FIELD_CURRENT_FEED_SPEED
#ifdef VARIABLE_SPINDLE
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
sprintf(temp, "|FS:%.1f,%.0f", st_get_realtime_rate(), sys.spindle_speed / MM_PER_INCH);
} else {
sprintf(temp, "|FS:%.0f,%.0f", st_get_realtime_rate(), sys.spindle_speed);
}
strcat(status, temp);
#else
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
sprintf(temp, "|F:%.1f", st_get_realtime_rate() / MM_PER_INCH);
} else {
sprintf(temp, "|F:%.0f", st_get_realtime_rate());
}
strcat(status, temp);
#endif
#endif
#ifdef REPORT_FIELD_PIN_STATE
uint8_t lim_pin_state = limits_get_state();
uint8_t ctrl_pin_state = system_control_get_state();
uint8_t prb_pin_state = probe_get_state();
if (lim_pin_state | ctrl_pin_state | prb_pin_state) {
strcat(status, "|Pn:");
if (prb_pin_state) { strcat(status, "P"); }
if (lim_pin_state) {
if (bit_istrue(lim_pin_state,bit(X_AXIS))) { strcat(status, "X"); }
if (bit_istrue(lim_pin_state,bit(Y_AXIS))) { strcat(status, "Y"); }
if (bit_istrue(lim_pin_state,bit(Z_AXIS))) { strcat(status, "Z"); }
#if (N_AXIS > A_AXIS)
if (bit_istrue(lim_pin_state,bit(A_AXIS))) { strcat(status, "A"); }
#endif
#if (N_AXIS > B_AXIS)
if (bit_istrue(lim_pin_state,bit(B_AXIS))) { strcat(status, "B"); }
#endif
#if (N_AXIS > C_AXIS)
if (bit_istrue(lim_pin_state,bit(C_AXIS))) { strcat(status, "C"); }
#endif
}
if (ctrl_pin_state) {
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_SAFETY_DOOR)) { strcat(status, "D"); }
#endif
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_RESET)) { strcat(status, "R"); }
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_FEED_HOLD)) { strcat(status, "H"); }
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_CYCLE_START)) { strcat(status, "S"); }
}
}
#endif
#ifdef REPORT_FIELD_WORK_COORD_OFFSET
if (sys.report_wco_counter > 0) { sys.report_wco_counter--; }
else {
if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
sys.report_wco_counter = (REPORT_WCO_REFRESH_BUSY_COUNT-1); // Reset counter for slow refresh
} else { sys.report_wco_counter = (REPORT_WCO_REFRESH_IDLE_COUNT-1); }
if (sys.report_ovr_counter == 0) { sys.report_ovr_counter = 1; } // Set override on next report.
strcat(status, "|WCO:");
report_util_axis_values(wco, temp);
strcat(status, temp);
}
#endif
#ifdef REPORT_FIELD_OVERRIDES
if (sys.report_ovr_counter > 0) { sys.report_ovr_counter--; }
else {
if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
sys.report_ovr_counter = (REPORT_OVR_REFRESH_BUSY_COUNT-1); // Reset counter for slow refresh
} else { sys.report_ovr_counter = (REPORT_OVR_REFRESH_IDLE_COUNT-1); }
sprintf(temp, "|Ov:%d,%d,%d", sys.f_override, sys.r_override, sys.spindle_speed_ovr);
strcat(status, temp);
uint8_t sp_state = spindle_get_state();
uint8_t cl_state = coolant_get_state();
if (sp_state || cl_state) {
strcat(status, "|A:");
if (sp_state) { // != SPINDLE_STATE_DISABLE
if (sp_state == SPINDLE_STATE_CW) { strcat(status, "S"); } // CW
else { strcat(status, "C"); } // CCW
}
if (cl_state & COOLANT_STATE_FLOOD) { strcat(status, "F"); }
#ifdef COOLANT_MIST_PIN // TODO Deal with M8 - Flood
if (cl_state & COOLANT_STATE_MIST) { strcat(status, "M"); }
#endif
}
}
#endif
#ifdef ENABLE_SD_CARD
if (get_sd_state(false) == SDCARD_BUSY_PRINTING) {
sprintf(temp, "|SD:%4.2f,", sd_report_perc_complete());
strcat(status, temp);
sd_get_current_filename(temp);
strcat(status, temp);
}
#endif
strcat(status, ">\r\n");
grbl_send(client, status);
}
void report_realtime_steps()
{
uint8_t idx;
for (idx=0; idx< N_AXIS; idx++) {
grbl_sendf(CLIENT_ALL, "%ld\n", sys_position[idx]); // OK to send to all ... debug stuff
}
}
void report_gcode_comment(char *comment) {
char msg[80];
const uint8_t offset = 4; // ignore "MSG_" part of comment
uint8_t index = offset;
if (strstr(comment, "MSG")) {
while(index < strlen(comment)) {
msg[index-offset] = comment[index];
index++;
}
msg[index-offset] = 0; // null terminate
grbl_sendf(CLIENT_ALL, "[MSG:GCode Comment %s]\r\n",msg);
}
}

View File

@ -0,0 +1,167 @@
/*
report.h - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 report_h
#define report_h
#include "grbl.h"
// Define Grbl status codes. Valid values (0-255)
#define STATUS_OK 0
#define STATUS_EXPECTED_COMMAND_LETTER 1
#define STATUS_BAD_NUMBER_FORMAT 2
#define STATUS_INVALID_STATEMENT 3
#define STATUS_NEGATIVE_VALUE 4
#define STATUS_SETTING_DISABLED 5
#define STATUS_SETTING_STEP_PULSE_MIN 6
#define STATUS_SETTING_READ_FAIL 7
#define STATUS_IDLE_ERROR 8
#define STATUS_SYSTEM_GC_LOCK 9
#define STATUS_SOFT_LIMIT_ERROR 10
#define STATUS_OVERFLOW 11
#define STATUS_MAX_STEP_RATE_EXCEEDED 12
#define STATUS_CHECK_DOOR 13
#define STATUS_LINE_LENGTH_EXCEEDED 14
#define STATUS_TRAVEL_EXCEEDED 15
#define STATUS_INVALID_JOG_COMMAND 16
#define STATUS_SETTING_DISABLED_LASER 17
#define STATUS_GCODE_UNSUPPORTED_COMMAND 20
#define STATUS_GCODE_MODAL_GROUP_VIOLATION 21
#define STATUS_GCODE_UNDEFINED_FEED_RATE 22
#define STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER 23
#define STATUS_GCODE_AXIS_COMMAND_CONFLICT 24
#define STATUS_GCODE_WORD_REPEATED 25
#define STATUS_GCODE_NO_AXIS_WORDS 26
#define STATUS_GCODE_INVALID_LINE_NUMBER 27
#define STATUS_GCODE_VALUE_WORD_MISSING 28
#define STATUS_GCODE_UNSUPPORTED_COORD_SYS 29
#define STATUS_GCODE_G53_INVALID_MOTION_MODE 30
#define STATUS_GCODE_AXIS_WORDS_EXIST 31
#define STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE 32
#define STATUS_GCODE_INVALID_TARGET 33
#define STATUS_GCODE_ARC_RADIUS_ERROR 34
#define STATUS_GCODE_NO_OFFSETS_IN_PLANE 35
#define STATUS_GCODE_UNUSED_WORDS 36
#define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 37
#define STATUS_GCODE_MAX_VALUE_EXCEEDED 38
#define STATUS_P_PARAM_MAX_EXCEEDED 39
#define STATUS_SD_FAILED_MOUNT 60 // SD Failed to mount
#define STATUS_SD_FAILED_READ 61 // SD Failed to read file
#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_FILE_EMPTY 64 // SD Card directory not found
#define STATUS_BT_FAIL_BEGIN 70 // Bluetooth failed to start
// Define Grbl alarm codes. Valid values (1-255). 0 is reserved.
#define ALARM_HARD_LIMIT_ERROR EXEC_ALARM_HARD_LIMIT
#define ALARM_SOFT_LIMIT_ERROR EXEC_ALARM_SOFT_LIMIT
#define ALARM_ABORT_CYCLE EXEC_ALARM_ABORT_CYCLE
#define ALARM_PROBE_FAIL_INITIAL EXEC_ALARM_PROBE_FAIL_INITIAL
#define ALARM_PROBE_FAIL_CONTACT EXEC_ALARM_PROBE_FAIL_CONTACT
#define ALARM_HOMING_FAIL_RESET EXEC_ALARM_HOMING_FAIL_RESET
#define ALARM_HOMING_FAIL_DOOR EXEC_ALARM_HOMING_FAIL_DOOR
#define ALARM_HOMING_FAIL_PULLOFF EXEC_ALARM_HOMING_FAIL_PULLOFF
#define ALARM_HOMING_FAIL_APPROACH EXEC_ALARM_HOMING_FAIL_APPROACH
// Define Grbl feedback message codes. Valid values (0-255).
#define MESSAGE_CRITICAL_EVENT 1
#define MESSAGE_ALARM_LOCK 2
#define MESSAGE_ALARM_UNLOCK 3
#define MESSAGE_ENABLED 4
#define MESSAGE_DISABLED 5
#define MESSAGE_SAFETY_DOOR_AJAR 6
#define MESSAGE_CHECK_LIMITS 7
#define MESSAGE_PROGRAM_END 8
#define MESSAGE_RESTORE_DEFAULTS 9
#define MESSAGE_SPINDLE_RESTORE 10
#define MESSAGE_SLEEP_MODE 11
#define MESSAGE_SD_FILE_QUIT 60 // mc_reset was called during an SD job
#define CLIENT_SERIAL 1
#define CLIENT_BT 2
#define CLIENT_WEBUI 3
#define CLIENT_TELNET 4
#define CLIENT_INPUT 5
#define CLIENT_ALL 0xFF
#define CLIENT_COUNT 5 // total number of client types regardless if they are used
// functions to send data to the user.
void grbl_send(uint8_t client, const char *text);
void grbl_sendf(uint8_t client, const char *format, ...);
//function to notify
void grbl_notify(const char *title, const char *msg);
void grbl_notifyf(const char *title, const char *format, ...);
// Prints system status messages.
void report_status_message(uint8_t status_code, uint8_t client);
void report_realtime_steps();
// Prints system alarm messages.
void report_alarm_message(uint8_t alarm_code);
// Prints miscellaneous feedback messages.
void report_feedback_message(uint8_t message_code);
// Prints welcome message
void report_init_message(uint8_t client);
// Prints Grbl help and current global settings
void report_grbl_help(uint8_t client);
// Prints Grbl global settings
void report_grbl_settings(uint8_t client);
// Prints an echo of the pre-parsed line received right before execution.
void report_echo_line_received(char *line, uint8_t client);
// Prints realtime status report
void report_realtime_status(uint8_t client);
// Prints recorded probe position
void report_probe_parameters(uint8_t client);
// Prints Grbl NGC parameters (coordinate offsets, probe)
void report_ngc_parameters(uint8_t client);
// Prints current g-code parser mode state
void report_gcode_modes(uint8_t client);
// Prints startup line when requested and executed.
void report_startup_line(uint8_t n, char *line, uint8_t client);
void report_execute_startup_message(char *line, uint8_t status_code, uint8_t client);
// Prints build info and user info
void report_build_info(char *line, uint8_t client);
void report_gcode_comment(char *comment);
#ifdef DEBUG
void report_realtime_debug();
#endif
#endif

View File

@ -0,0 +1,348 @@
/*
serial.cpp - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
#include "commands.h"
#define RX_RING_BUFFER (RX_BUFFER_SIZE+1)
#define TX_RING_BUFFER (TX_BUFFER_SIZE+1)
portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED;
uint8_t serial_rx_buffer[CLIENT_COUNT][RX_RING_BUFFER];
uint8_t serial_rx_buffer_head[CLIENT_COUNT] = {0};
volatile uint8_t serial_rx_buffer_tail[CLIENT_COUNT] = {0};
static TaskHandle_t serialCheckTaskHandle = 0;
// Returns the number of bytes available in the RX serial buffer.
uint8_t serial_get_rx_buffer_available(uint8_t client)
{
uint8_t client_idx = client - 1;
uint8_t rtail = serial_rx_buffer_tail[client_idx]; // Copy to limit multiple calls to volatile
if (serial_rx_buffer_head[client_idx] >= rtail) { return(RX_BUFFER_SIZE - (serial_rx_buffer_head[client_idx]-rtail)); }
return((rtail-serial_rx_buffer_head[client_idx]-1));
}
void serial_init()
{
Serial.begin(BAUD_RATE);
grbl_send(CLIENT_SERIAL,"\r\n"); // create some white space after ESP32 boot info
serialCheckTaskHandle = 0;
// create a task to check for incoming data
xTaskCreatePinnedToCore( serialCheckTask, // task
"serialCheckTask", // name for task
8192, // size of task stack
NULL, // parameters
1, // priority
&serialCheckTaskHandle,
0 // core
);
}
// this task runs and checks for data on all interfaces
// REaltime stuff is acted upon, then characters are added to the appropriate buffer
void serialCheckTask(void *pvParameters)
{
uint8_t data = 0;
uint8_t next_head;
uint8_t client = CLIENT_ALL; // who send the data
uint8_t client_idx = 0; // index of data buffer
while(true) // run continuously
{
while (Serial.available() || inputBuffer.available()
#ifdef ENABLE_BLUETOOTH
|| (SerialBT.hasClient() && SerialBT.available())
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
|| Serial2Socket.available()
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_TELNET)
|| telnet_server.available()
#endif
)
{
if (Serial.available())
{
client = CLIENT_SERIAL;
data = Serial.read();
}
else if (inputBuffer.available()){
client = CLIENT_INPUT;
data = inputBuffer.read();
}
else
{ //currently is wifi or BT but better to prepare both can be live
#ifdef ENABLE_BLUETOOTH
if(SerialBT.hasClient() && SerialBT.available()){
client = CLIENT_BT;
data = SerialBT.read();
//Serial.write(data); // echo all data to serial
} else {
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
if (Serial2Socket.available()) {
client = CLIENT_WEBUI;
data = Serial2Socket.read();
}
else
{
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_TELNET)
if(telnet_server.available()){
client = CLIENT_TELNET;
data = telnet_server.read();
}
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
}
#endif
#ifdef ENABLE_BLUETOOTH
}
#endif
}
client_idx = client - 1; // for zero based array
// Pick off realtime command characters directly from the serial stream. These characters are
// not passed into the main buffer, but these set system state flag bits for realtime execution.
switch (data) {
case CMD_RESET:
mc_reset(); // Call motion control reset routine.
//report_init_message(client); // fool senders into thinking a reset happened.
break;
case CMD_STATUS_REPORT:
report_realtime_status(client);
break; // direct call instead of setting flag
case CMD_CYCLE_START: system_set_exec_state_flag(EXEC_CYCLE_START); break; // Set as true
case CMD_FEED_HOLD: system_set_exec_state_flag(EXEC_FEED_HOLD); break; // Set as true
default :
if (data > 0x7F) { // Real-time control characters are extended ACSII only.
switch(data) {
case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true
case CMD_JOG_CANCEL:
if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel.
system_set_exec_state_flag(EXEC_MOTION_CANCEL);
}
break;
#ifdef DEBUG
case CMD_DEBUG_REPORT: {uint8_t sreg = SREG; cli(); bit_true(sys_rt_exec_debug,EXEC_DEBUG_REPORT); SREG = sreg;} break;
#endif
case CMD_FEED_OVR_RESET: system_set_exec_motion_override_flag(EXEC_FEED_OVR_RESET); break;
case CMD_FEED_OVR_COARSE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_PLUS); break;
case CMD_FEED_OVR_COARSE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_MINUS); break;
case CMD_FEED_OVR_FINE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_PLUS); break;
case CMD_FEED_OVR_FINE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_MINUS); break;
case CMD_RAPID_OVR_RESET: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_RESET); break;
case CMD_RAPID_OVR_MEDIUM: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_MEDIUM); break;
case CMD_RAPID_OVR_LOW: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_LOW); break;
case CMD_SPINDLE_OVR_RESET: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_RESET); break;
case CMD_SPINDLE_OVR_COARSE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS); break;
case CMD_SPINDLE_OVR_COARSE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS); break;
case CMD_SPINDLE_OVR_FINE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_PLUS); break;
case CMD_SPINDLE_OVR_FINE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS); break;
case CMD_SPINDLE_OVR_STOP: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); break;
#ifdef COOLANT_FLOOD_PIN
case CMD_COOLANT_FLOOD_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE); break;
#endif
#ifdef COOLANT_MIST_PIN
case CMD_COOLANT_MIST_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE); break;
#endif
}
// Throw away any unfound extended-ASCII character by not passing it to the serial buffer.
} else { // Write character to buffer
vTaskEnterCritical(&myMutex);
next_head = serial_rx_buffer_head[client_idx] + 1;
if (next_head == RX_RING_BUFFER) { next_head = 0; }
// Write data to buffer unless it is full.
if (next_head != serial_rx_buffer_tail[client_idx]) {
serial_rx_buffer[client_idx][serial_rx_buffer_head[client_idx]] = data;
serial_rx_buffer_head[client_idx] = next_head;
}
vTaskExitCritical(&myMutex);
}
} // switch data
} // if something available
COMMANDS::handle();
#ifdef ENABLE_WIFI
wifi_config.handle();
#endif
#ifdef ENABLE_BLUETOOTH
bt_config.handle();
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
Serial2Socket.handle_flush();
#endif
vTaskDelay(1 / portTICK_RATE_MS); // Yield to other tasks
} // while(true)
}
// ==================== call this in main protocol loop if you want it in the main task =========
// be sure to stop task.
// Realtime stuff is acted upon, then characters are added to the appropriate buffer
void serialCheck()
{
uint8_t data = 0;
uint8_t next_head;
uint8_t client = CLIENT_SERIAL; // who send the data
uint8_t client_idx = 0; // index of data buffer
while (Serial.available() || inputBuffer.available()
#ifdef ENABLE_BLUETOOTH
|| (SerialBT.hasClient() && SerialBT.available())
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
|| Serial2Socket.available()
#endif
)
{
if (Serial.available())
{
client = CLIENT_SERIAL;
data = Serial.read();
}
else if (inputBuffer.available())
{
client = CLIENT_INPUT;
data = inputBuffer.read();
}
#if defined (ENABLE_BLUETOOTH) || (defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN))
else
{ //currently is wifi or BT but better to prepare both can be live
#ifdef ENABLE_BLUETOOTH
if(SerialBT.hasClient() && SerialBT.available()){
client = CLIENT_BT;
data = SerialBT.read();
} else {
#endif
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP) && defined(ENABLE_SERIAL2SOCKET_IN)
client = CLIENT_WEBUI;
data = Serial2Socket.read();
#endif
#ifdef ENABLE_BLUETOOTH
}
#endif
}
#endif
client_idx = client - 1; // for zero based array
// Pick off realtime command characters directly from the serial stream. These characters are
// not passed into the main buffer, but these set system state flag bits for realtime execution.
switch (data) {
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
case CMD_STATUS_REPORT:
report_realtime_status(client);
break; // direct call instead of setting flag
case CMD_CYCLE_START: system_set_exec_state_flag(EXEC_CYCLE_START); break; // Set as true
case CMD_FEED_HOLD: system_set_exec_state_flag(EXEC_FEED_HOLD); break; // Set as true
default :
if (data > 0x7F) { // Real-time control characters are extended ACSII only.
switch(data) {
case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true
case CMD_JOG_CANCEL:
if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel.
system_set_exec_state_flag(EXEC_MOTION_CANCEL);
}
break;
#ifdef DEBUG
case CMD_DEBUG_REPORT: {uint8_t sreg = SREG; cli(); bit_true(sys_rt_exec_debug,EXEC_DEBUG_REPORT); SREG = sreg;} break;
#endif
case CMD_FEED_OVR_RESET: system_set_exec_motion_override_flag(EXEC_FEED_OVR_RESET); break;
case CMD_FEED_OVR_COARSE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_PLUS); break;
case CMD_FEED_OVR_COARSE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_MINUS); break;
case CMD_FEED_OVR_FINE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_PLUS); break;
case CMD_FEED_OVR_FINE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_MINUS); break;
case CMD_RAPID_OVR_RESET: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_RESET); break;
case CMD_RAPID_OVR_MEDIUM: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_MEDIUM); break;
case CMD_RAPID_OVR_LOW: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_LOW); break;
case CMD_SPINDLE_OVR_RESET: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_RESET); break;
case CMD_SPINDLE_OVR_COARSE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS); break;
case CMD_SPINDLE_OVR_COARSE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS); break;
case CMD_SPINDLE_OVR_FINE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_PLUS); break;
case CMD_SPINDLE_OVR_FINE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS); break;
case CMD_SPINDLE_OVR_STOP: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); break;
#ifdef COOLANT_FLOOD_PIN
case CMD_COOLANT_FLOOD_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE); break;
#endif
#ifdef COOLANT_MIST_PIN
case CMD_COOLANT_MIST_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE); break;
#endif
}
// Throw away any unfound extended-ASCII character by not passing it to the serial buffer.
} else { // Write character to buffer
next_head = serial_rx_buffer_head[client_idx] + 1;
if (next_head == RX_RING_BUFFER) { next_head = 0; }
// Write data to buffer unless it is full.
if (next_head != serial_rx_buffer_tail[client_idx]) {
serial_rx_buffer[client_idx][serial_rx_buffer_head[client_idx]] = data;
serial_rx_buffer_head[client_idx] = next_head;
}
}
} // switch data
} // if something available
}
void serial_reset_read_buffer(uint8_t client)
{
for (uint8_t client_num = 0; client_num <= CLIENT_COUNT; client_num++)
{
if (client == client_num || client == CLIENT_ALL)
{
serial_rx_buffer_tail[client_num-1] = serial_rx_buffer_head[client_num-1];
}
}
}
// Writes one byte to the TX serial buffer. Called by main program.
void serial_write(uint8_t data) {
Serial.write((char)data);
}
// Fetches the first byte in the serial read buffer. Called by main program.
uint8_t serial_read(uint8_t client)
{
uint8_t client_idx = client - 1;
uint8_t tail = serial_rx_buffer_tail[client_idx]; // Temporary serial_rx_buffer_tail (to optimize for volatile)
if (serial_rx_buffer_head[client_idx] == tail) {
return SERIAL_NO_DATA;
} else {
vTaskEnterCritical(&myMutex); // make sure buffer is not modified while reading by newly read chars from the serial when we are here
uint8_t data = serial_rx_buffer[client_idx][tail];
tail++;
if (tail == RX_RING_BUFFER) { tail = 0; }
serial_rx_buffer_tail[client_idx] = tail;
vTaskExitCritical(&myMutex);
return data;
}
}

View File

@ -0,0 +1,57 @@
/*
serial.h - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 serial_h
#define serial_h
#include "grbl.h"
#ifndef RX_BUFFER_SIZE
#define RX_BUFFER_SIZE 128
#endif
#ifndef TX_BUFFER_SIZE
#ifdef USE_LINE_NUMBERS
#define TX_BUFFER_SIZE 112
#else
#define TX_BUFFER_SIZE 104
#endif
#endif
#define SERIAL_NO_DATA 0xff
// a task to read for incoming data from serial port
void serialCheckTask(void *pvParameters);
void serialCheck();
void serial_write(uint8_t data);
// Fetches the first byte in the serial read buffer. Called by main program.
uint8_t serial_read(uint8_t client);
// See if the character is an action command like feedhold or jogging. If so, do the action and return true
uint8_t check_action_command(uint8_t data);
void serial_init();
void serial_reset_read_buffer(uint8_t client);
// Returns the number of bytes available in the RX serial buffer.
uint8_t serial_get_rx_buffer_available(uint8_t client);
#endif

View File

@ -0,0 +1,181 @@
/*
serial2socket.cpp - serial 2 socket functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef ARDUINO_ARCH_ESP32
//#include "grbl.h"
#include "config.h"
#if defined (ENABLE_WIFI) && defined(ENABLE_HTTP)
#include "serial2socket.h"
#include "web_server.h"
#include <WebSocketsServer.h>
#include <WiFi.h>
Serial_2_Socket Serial2Socket;
Serial_2_Socket::Serial_2_Socket(){
_web_socket = NULL;
_TXbufferSize = 0;
_RXbufferSize = 0;
_RXbufferpos = 0;
}
Serial_2_Socket::~Serial_2_Socket(){
if (_web_socket) detachWS();
_TXbufferSize = 0;
_RXbufferSize = 0;
_RXbufferpos = 0;
}
void Serial_2_Socket::begin(long speed){
_TXbufferSize = 0;
_RXbufferSize = 0;
_RXbufferpos = 0;
}
void Serial_2_Socket::end(){
_TXbufferSize = 0;
_RXbufferSize = 0;
_RXbufferpos = 0;
}
long Serial_2_Socket::baudRate(){
return 0;
}
bool Serial_2_Socket::attachWS(void * web_socket){
if (web_socket) {
_web_socket = web_socket;
_TXbufferSize=0;
return true;
}
return false;
}
bool Serial_2_Socket::detachWS(){
_web_socket = NULL;
return true;
}
Serial_2_Socket::operator bool() const
{
return true;
}
int Serial_2_Socket::available(){
return _RXbufferSize;
}
size_t Serial_2_Socket::write(uint8_t c)
{
if(!_web_socket) return 0;
write(&c,1);
return 1;
}
size_t Serial_2_Socket::write(const uint8_t *buffer, size_t size)
{
if((buffer == NULL) ||(!_web_socket)) {
if(buffer == NULL){
log_i("[SOCKET]No buffer");
}
if(!_web_socket){
log_i("[SOCKET]No socket");
}
return 0;
}
#if defined(ENABLE_SERIAL2SOCKET_OUT)
if (_TXbufferSize==0)_lastflush = millis();
//send full line
if (_TXbufferSize + size > TXBUFFERSIZE) flush();
//need periodic check to force to flush in case of no end
for (int i = 0; i < size;i++){
_TXbuffer[_TXbufferSize] = buffer[i];
_TXbufferSize++;
}
log_i("[SOCKET]buffer size %d",_TXbufferSize);
handle_flush();
#endif
return size;
}
int Serial_2_Socket::peek(void){
if (_RXbufferSize > 0)return _RXbuffer[_RXbufferpos];
else return -1;
}
bool Serial_2_Socket::push (const char * data){
#if defined(ENABLE_SERIAL2SOCKET_IN)
int data_size = strlen(data);
if ((data_size + _RXbufferSize) <= RXBUFFERSIZE){
int current = _RXbufferpos + _RXbufferSize;
if (current > RXBUFFERSIZE) current = current - RXBUFFERSIZE;
for (int i = 0; i < data_size; i++){
if (current > (RXBUFFERSIZE-1)) current = 0;
_RXbuffer[current] = data[i];
current ++;
}
_RXbufferSize+=strlen(data);
return true;
}
return false;
#else
return true;
#endif
}
int Serial_2_Socket::read(void){
if (_RXbufferSize > 0) {
int v = _RXbuffer[_RXbufferpos];
_RXbufferpos++;
if (_RXbufferpos > (RXBUFFERSIZE-1))_RXbufferpos = 0;
_RXbufferSize--;
return v;
} else return -1;
}
void Serial_2_Socket::handle_flush() {
if (_TXbufferSize > 0) {
if ((_TXbufferSize>=TXBUFFERSIZE) || ((millis()- _lastflush) > FLUSHTIMEOUT)) {
log_i("[SOCKET]need flush, buffer size %d",_TXbufferSize);
flush();
}
}
}
void Serial_2_Socket::flush(void){
if (_TXbufferSize > 0){
//if ((((AsyncWebSocket *)_web_socket)->count() > 0) && (((AsyncWebSocket *)_web_socket)->availableForWriteAll())) {
log_i("[SOCKET]flush data, buffer size %d",_TXbufferSize);
((WebSocketsServer *)_web_socket)->broadcastBIN(_TXbuffer,_TXbufferSize);
// } else {
// log_i("[SOCKET]Cannot flush, buffer size %d",_TXbufferSize);
// }
//refresh timout
_lastflush = millis();
//reset buffer
_TXbufferSize = 0;
}
}
#endif // ENABLE_WIFI
#endif // ARDUINO_ARCH_ESP32

View File

@ -0,0 +1,81 @@
/*
serial2socket.h - serial 2 socket functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SERIAL_2_SOCKET_H_
#define _SERIAL_2_SOCKET_H_
#include "Print.h"
#define TXBUFFERSIZE 1200
#define RXBUFFERSIZE 128
#define FLUSHTIMEOUT 500
class Serial_2_Socket: public Print{
public:
Serial_2_Socket();
~Serial_2_Socket();
size_t write(uint8_t c);
size_t write(const uint8_t *buffer, size_t size);
inline size_t write(const char * s)
{
return write((uint8_t*) s, strlen(s));
}
inline size_t write(unsigned long n)
{
return write((uint8_t) n);
}
inline size_t write(long n)
{
return write((uint8_t) n);
}
inline size_t write(unsigned int n)
{
return write((uint8_t) n);
}
inline size_t write(int n)
{
return write((uint8_t) n);
}
long baudRate();
void begin(long speed);
void end();
int available();
int peek(void);
int read(void);
bool push (const char * data);
void flush(void);
void handle_flush();
operator bool() const;
bool attachWS(void * web_socket);
bool detachWS();
private:
uint32_t _lastflush;
void * _web_socket;
uint8_t _TXbuffer[TXBUFFERSIZE];
uint16_t _TXbufferSize;
uint8_t _RXbuffer[RXBUFFERSIZE];
uint16_t _RXbufferSize;
uint16_t _RXbufferpos;
};
extern Serial_2_Socket Serial2Socket;
#endif

View File

@ -0,0 +1,362 @@
/*
servo_axis.cpp
Part of Grbl_ESP32
copyright (c) 2018 - Bart Dring. This file was intended for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
See servo_axis.h for more details
*/
#include "grbl.h"
#ifdef USE_SERVO_AXES
static TaskHandle_t servosSyncTaskHandle = 0;
#ifdef SERVO_X_PIN
ServoAxis X_Servo_Axis(X_AXIS, SERVO_X_PIN, SERVO_X_CHANNEL_NUM);
#endif
#ifdef SERVO_Y_PIN
ServoAxis Y_Servo_Axis(Y_AXIS, SERVO_Y_PIN, SERVO_Y_CHANNEL_NUM);
#endif
#ifdef SERVO_Z_PIN
ServoAxis Z_Servo_Axis(Z_AXIS, SERVO_Z_PIN, SERVO_Z_CHANNEL_NUM);
#endif
#ifdef SERVO_A_PIN
ServoAxis A_Servo_Axis(A_AXIS, SERVO_A_PIN, SERVO_A_CHANNEL_NUM);
#endif
#ifdef SERVO_B_PIN
ServoAxis B_Servo_Axis(B_AXIS, SERVO_B_PIN, SERVO_B_CHANNEL_NUM);
#endif
#ifdef SERVO_C_PIN
ServoAxis C_Servo_Axis(C_AXIS, SERVO_C_PIN, SERVO_C_CHANNEL_NUM);
#endif
void init_servos()
{
//grbl_send(CLIENT_SERIAL, "[MSG: Init Servos]\r\n");
#ifdef SERVO_X_PIN
grbl_sendf(CLIENT_SERIAL, "[MSG:X Servo range %4.3f to %4.3f]\r\n", SERVO_X_RANGE_MIN, SERVO_X_RANGE_MAX);
X_Servo_Axis.init();
X_Servo_Axis.set_range(SERVO_X_RANGE_MIN, SERVO_X_RANGE_MAX);
X_Servo_Axis.set_homing_type(SERVO_HOMING_OFF);
X_Servo_Axis.set_disable_on_alarm(false);
X_Servo_Axis.set_disable_with_steppers(false);
#endif
#ifdef SERVO_Y_PIN
grbl_sendf(CLIENT_SERIAL, "[MSG:Y Servo range %4.3f to %4.3f]\r\n", SERVO_Y_RANGE_MIN, SERVO_Y_RANGE_MAX);
Y_Servo_Axis.init();
Y_Servo_Axis.set_range(SERVO_Y_RANGE_MIN, SERVO_Y_RANGE_MAX);
#endif
#ifdef SERVO_Z_PIN
grbl_sendf(CLIENT_SERIAL, "[MSG:Z Servo range %4.3f to %4.3f]\r\n", SERVO_Z_RANGE_MIN, SERVO_Z_RANGE_MAX);
Z_Servo_Axis.init();
Z_Servo_Axis.set_range(SERVO_Z_RANGE_MIN, SERVO_Z_RANGE_MAX);
#ifdef SERVO_Z_HOMING_TYPE
Z_Servo_Axis.set_homing_type(SERVO_Z_HOMING_TYPE);
#endif
#ifdef SERVO_Z_HOME_POS
Z_Servo_Axis.set_homing_position(SERVO_Z_HOME_POS);
#endif
#ifdef SERVO_Z_MPOS // value should be true or false
Z_Servo_Axis.set_use_mpos(SERVO_Z_MPOS);
#endif
#endif
#ifdef SERVO_A_PIN
grbl_sendf(CLIENT_SERIAL, "[MSG:A Servo range %4.3f to %4.3f]\r\n", SERVO_A_RANGE_MIN, SERVO_A_RANGE_MAX);
A_Servo_Axis.init();
A_Servo_Axis.set_range(SERVO_A_RANGE_MIN, SERVO_A_RANGE_MAX);
A_Servo_Axis.set_homing_type(SERVO_HOMING_OFF);
A_Servo_Axis.set_disable_on_alarm(false);
A_Servo_Axis.set_disable_with_steppers(false);
#endif
#ifdef SERVO_B_PIN
grbl_sendf(CLIENT_SERIAL, "[MSG:B Servo range %4.3f to %4.3f]\r\n", SERVO_B_RANGE_MIN, SERVO_B_RANGE_MAX);
B_Servo_Axis.init();
B_Servo_Axis.set_range(SERVO_B_RANGE_MIN, SERVO_B_RANGE_MAX);
#endif
#ifdef SERVO_C_PIN
grbl_sendf(CLIENT_SERIAL, "[MSG:C Servo range %4.3f to %4.3f]\r\n", SERVO_C_RANGE_MIN, SERVO_C_RANGE_MAX);
C_Servo_Axis.init();
C_Servo_Axis.set_range(SERVO_C_RANGE_MIN, SERVO_C_RANGE_MAX);
//C_Servo_Axis.set_homing_type(SERVO_HOMING_TARGET);
//C_Servo_Axis.set_homing_position(SERVO_C_RANGE_MAX);
#ifdef SERVO_C_HOMING_TYPE
C_Servo_Axis.set_homing_type(SERVO_C_HOMING_TYPE);
#endif
#ifdef SERVO_C_HOME_POS
C_Servo_Axis.set_homing_position(SERVO_C_HOME_POS);
#endif
#ifdef SERVO_C_MPOS // value should be true or false
C_Servo_Axis.set_use_mpos(SERVO_C_MPOS);
#endif
#endif
// setup a task that will calculate the determine and set the servo positions
xTaskCreatePinnedToCore( servosSyncTask, // task
"servosSyncTask", // name for task
4096, // size of task stack
NULL, // parameters
1, // priority
&servosSyncTaskHandle,
0 // core
);
}
// this is the task
void servosSyncTask(void *pvParameters)
{
TickType_t xLastWakeTime;
const TickType_t xServoFrequency = 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
#ifdef SERVO_X_PIN
X_Servo_Axis.set_location();
#endif
#ifdef SERVO_Y_PIN
Y_Servo_Axis.set_location();
#endif
#ifdef SERVO_Z_PIN
Z_Servo_Axis.set_location();
#endif
#ifdef SERVO_A_PIN
A_Servo_Axis.set_location();
#endif
#ifdef SERVO_B_PIN
B_Servo_Axis.set_location();
#endif
#ifdef SERVO_C_PIN
C_Servo_Axis.set_location();
#endif
vTaskDelayUntil(&xLastWakeTime, xServoFrequency);
}
}
// =============================== Class Stuff ================================= //
ServoAxis::ServoAxis(uint8_t axis, uint8_t pin_num, uint8_t channel_num) // constructor
{
_axis = axis;
_pin_num = pin_num;
_channel_num = channel_num;
_showError = true; // this will be used to show calibration error only once
_use_mpos = true; // default is to use the machine position rather than work position
}
void ServoAxis::init()
{
_cal_is_valid();
ledcSetup(_channel_num, _pwm_freq, _pwm_resolution_bits);
ledcAttachPin(_pin_num, _channel_num);
disable();
}
void ServoAxis::set_location()
{
// These are the pulse lengths for the minimum and maximum positions
// Note: Some machines will have the physical max/min inverted with pulse length max/min due to invert setting $3=...
float servo_pulse_min, servo_pulse_max;
float min_pulse_cal, max_pulse_cal; // calibration values in percent 110% = 1.1
uint32_t servo_pulse_len;
float servo_pos, mpos, offset;
// skip location if we are in alarm mode
if (_disable_on_alarm && (sys.state == STATE_ALARM)) {
disable();
return;
}
// track the disable status of the steppers if desired.
if (_disable_with_steppers && get_stepper_disable()) {
disable();
return;
}
if ( (_homing_type == SERVO_HOMING_TARGET) && (sys.state == STATE_HOMING) ) {
servo_pos = _homing_position; // go to servos home position
}
else {
mpos = system_convert_axis_steps_to_mpos(sys_position, _axis); // get the axis machine position in mm
if (_use_mpos) {
servo_pos = mpos;
}
else {
offset = gc_state.coord_system[_axis] + gc_state.coord_offset[_axis]; // get the current axis work offset
servo_pos = mpos - offset; // determine the current work position
}
}
// 1. Get the pulse ranges of the servos
// 2. Invert if selected in the settings
// 3. Get the calibration values from the settings
// 4. Adjust the calibration offset direction of the cal based on the direction
// 5. Apply the calibrarion
servo_pulse_min = SERVO_MIN_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
swap(servo_pulse_min, servo_pulse_max);
}
// get the calibration values
if (_cal_is_valid()) { // if calibration settings are OK then apply them
// apply a calibration
// 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
min_pulse_cal = 2.0 - (settings.steps_per_mm[_axis] / 100.0);
max_pulse_cal = (settings.max_travel[_axis] / -100.0);
}
else { // inverted direction
min_pulse_cal = (settings.steps_per_mm[_axis] / 100.0);
max_pulse_cal = 2.0 - (settings.max_travel[_axis] / -100.0);
}
}
else { // settings are not valid so don't apply any calibration
min_pulse_cal = 1.0;
max_pulse_cal = 1.0;
}
// apply the calibrations
servo_pulse_min *= min_pulse_cal;
servo_pulse_max *= max_pulse_cal;
// determine the pulse length
servo_pulse_len = (uint32_t)mapConstrain(servo_pos, _position_min, _position_max, servo_pulse_min, servo_pulse_max );
_write_pwm(servo_pulse_len);
}
void ServoAxis::_write_pwm(uint32_t duty)
{
if (ledcRead(_channel_num) != duty) { // only write if it is changing
ledcWrite(_channel_num, duty);
}
}
// sets the PWM to zero. This allows most servos to be manually moved
void ServoAxis::disable()
{
_write_pwm(0);
}
// checks to see if calibration values are in an acceptable range
// vebose = true if you want an error sent to serial port
bool ServoAxis::_cal_is_valid()
{
bool settingsOK = true;
if ( (settings.steps_per_mm[_axis] < SERVO_CAL_MIN) || (settings.steps_per_mm[_axis] > SERVO_CAL_MAX) ) {
if (_showError) {
grbl_sendf(CLIENT_SERIAL, "[MSG:Servo calibration ($10%d) value error. Reset to 100]\r\n", _axis);
settings.steps_per_mm[_axis] = 100;
write_global_settings();
}
settingsOK = false;
}
// 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) ) {
if (_showError) {
grbl_sendf(CLIENT_SERIAL, "[MSG:Servo calibration ($13%d) value error. Reset to 100]\r\n", _axis);
settings.max_travel[_axis] = -100;
write_global_settings();
}
settingsOK = false;
}
_showError = false; // to show error once
if (! settingsOK) {
write_global_settings(); // they were changed so write them to
}
return settingsOK;
}
/*
Use this to set the max and min position in mm of the servo
This is used when mapping pulse length to the position
*/
void ServoAxis::set_range(float min, float max) {
if (min < max) {
_position_min = min;
_position_max = max;
}
else {
grbl_send(CLIENT_SERIAL, "[MSG:Error setting range. Min not smaller than max]\r\n");
}
}
/*
Sets the mode the servo will be in during homing
See servo_axis.h for SERVO_HOMING_xxxxx types
*/
void ServoAxis::set_homing_type(uint8_t homing_type)
{
if (homing_type <= SERVO_HOMING_TARGET)
_homing_type = homing_type;
}
/*
Use this to set the homing position the servo will be commanded to go if
the current homing mode is SERVO_HOMING_TARGET
*/
void ServoAxis::set_homing_position(float homing_position)
{
_homing_position = homing_position;
}
/*
Use this to set the disable on alarm feature. If true, then hobby servo PWM
will be disable in Grbl alarm mode (like before homing). Typical hobby servo
can be moved by hand in this mode
*/
void ServoAxis::set_disable_on_alarm (bool disable_on_alarm)
{
_disable_on_alarm = disable_on_alarm;
}
void ServoAxis::set_disable_with_steppers(bool disable_with_steppers) {
_disable_with_steppers = disable_with_steppers;
}
/*
If true, servo position will alway be calculated in machine position
Offsets will not be applied
*/
void ServoAxis::set_use_mpos(bool use_mpos) {
_use_mpos = use_mpos;
}
#endif

View File

@ -0,0 +1,133 @@
/*
solenoid_pen.h
Part of Grbl_ESP32
copyright (c) 2019 - Bart Dring. This file was intended for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
Servo Axis Class
The Servo axis feature allows you to use a hobby servo on any axis.
This is done using a repeating RTOS task. Grbl continues to calculate
the position of the axis in real time. The task looks at the current position of
the axis and calculates the required PWM value to go to that location. You define the travel
of the servo in millimeters.
Grbl still uses the acceleration and speed values you have in the settings, so it
will coordinate servo axes with stepper motor axes. This assumes these values are within the
capabilities of the servo
Usage
1. In config.h un-comment #define USE_SERVO_AXES
2. In a cpu_map.h section, define servo pins and PWM channels like this ....
#define SERVO_Y_PIN GPIO_NUM_14
#define SERVO_Y_CHANNEL_NUM 6
undefine any step and direction pins associated with that axis
3. In servo_axis.cpp init_servos() function, configure servos like this ....
X_Servo_Axis.set_range(0.0, 20.0); // millimeter
X_Servo_Axis.set_homing_type(SERVO_HOMING_OFF);
X_Servo_Axis.set_disable_on_alarm(true);
The positions can be calibrated using the settings. $10x (resolution) settings adjust the minimum
position and $13x (max travel) settings adjust the maximum position. If the servo is traveling
backwards from what you want, you can use the $3 direction setting to compensate.
*/
#ifndef servo_axis_h
#define servo_axis_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 // Hz This is the task frequency
#define SERVO_HOMING_OFF 0 // servo is off during homing
#define SERVO_HOMING_TARGET 1 // servo is send to a location during homing
extern float my_location;
void init_servos();
void servosSyncTask(void *pvParameters);
class ServoAxis{
public:
ServoAxis(uint8_t axis, uint8_t pin_num, uint8_t channel_num); // constructor
void init();
void set_location();
void disable(); // sets PWM to 0% duty cycle. Most servos can be manually moved in this state
void set_range(float min, float max);
void set_homing_type(uint8_t homing_type);
void set_homing_position(float homing_position);
void set_disable_on_alarm (bool disable_on_alarm);
void set_disable_with_steppers(bool disable_with_steppers);
void set_use_mpos(bool use_mpos);
private:
int _axis; // these should be assign in constructor using Grbl X_AXIS type values
int _pin_num; // The GPIO pin being used
int _channel_num; // The PWM channel
bool _showError;
uint32_t _pwm_freq = SERVO_PULSE_FREQ;
uint32_t _pwm_resolution_bits = SERVO_PULSE_RES_BITS;
float _pulse_min = SERVO_MIN_PULSE; // in pwm counts
float _pulse_max = SERVO_MAX_PULSE; // in pwm counts
float _position_min = SERVO_POSITION_MIN_DEFAULT; // position in millimeters
float _position_max = SERVO_POSITION_MAX_DEFAULT; // position in millimeters
uint8_t _homing_type = SERVO_HOMING_OFF;
float _homing_position = SERVO_POSITION_MAX_DEFAULT;
bool _disable_on_alarm = true;
bool _disable_with_steppers = false;
bool _use_mpos = true;
bool _validate_cal_settings();
void _write_pwm(uint32_t duty);
bool _cal_is_valid(); // checks to see if calibration values are in acceptable range
};
#endif

View File

@ -0,0 +1,176 @@
/*
servo_pen.cpp
Part of Grbl_ESP32
copyright (c) 2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
#ifdef USE_PEN_SERVO
static TaskHandle_t servoSyncTaskHandle = 0;
// used to delay turn on
bool servo_pen_enable = false;
void servo_init()
{
grbl_send(CLIENT_SERIAL, "[MSG:Servo Pen Mode]\r\n"); // startup message
//validate_servo_settings(true); // display any calibration errors
// Debug stuff
//grbl_sendf(CLIENT_SERIAL, "[MSG:Servo max,min pulse times %.4f sec,%.4f sec]\r\n", SERVO_MAX_PULSE_SEC, SERVO_MIN_PULSE_SEC);
//grbl_sendf(CLIENT_SERIAL, "[MSG:Servo max,min pulse counts %d,%d]\r\n", SERVO_MAX_PULSE, SERVO_MIN_PULSE);
validate_servo_settings(true); // will print errors
// debug stuff
servo_pen_enable = false; // start delay has not completed yet.
// setup PWM channel
ledcSetup(SERVO_PEN_CHANNEL_NUM, SERVO_PULSE_FREQ, SERVO_PULSE_RES_BITS);
ledcAttachPin(SERVO_PEN_PIN, SERVO_PEN_CHANNEL_NUM);
servo_disable(); // start it it off
// setup a task that will calculate the determine and set the servo position
xTaskCreatePinnedToCore( servoSyncTask, // task
"servoSyncTask", // name for task
4096, // size of task stack
NULL, // parameters
1, // priority
&servoSyncTaskHandle,
0 // core
);
}
// turn off the PWM (0 duty) to prevent servo jitter when not in use.
void servo_disable()
{
ledcWrite(SERVO_PEN_CHANNEL_NUM, 0);
}
// Grbl settings are used to calibrate the servo positions
// They work on a percentage, so a value of 100 (100%) applies no calibration
// Values outside a reasonable range can cause errors, so this function checks
// that they are within a reasonable range
bool validate_servo_settings(bool verbose) // make sure the settings are reasonable..otherwise reset the settings to default
{
bool settingsOK = true;
if ( (settings.steps_per_mm[Z_AXIS] < SERVO_CAL_MIN) || (settings.steps_per_mm[Z_AXIS] > SERVO_CAL_MAX) ) {
if (verbose) {
grbl_sendf(CLIENT_SERIAL, "[MSG:Servo cal ($102) Error: %4.4f s/b between %.2f and %.2f]\r\n", settings.steps_per_mm[Z_AXIS], SERVO_CAL_MIN, SERVO_CAL_MAX);
}
settingsOK = false;
}
// Note: Max travel is set positive via $$, but stored as a negative number
if ( (settings.max_travel[Z_AXIS] < -SERVO_CAL_MAX) || (settings.max_travel[Z_AXIS] > -SERVO_CAL_MIN) ) {
if (verbose) {
grbl_sendf(CLIENT_SERIAL, "[MSG:Servo cal ($132) Error: %4.4f s/b between %.2f and %.2f]\r\n", -settings.max_travel[Z_AXIS], SERVO_CAL_MIN, SERVO_CAL_MAX);
}
settingsOK = false;
}
return settingsOK;
}
// this is the task
void servoSyncTask(void *pvParameters)
{
//int32_t current_position[N_AXIS]; // copy of current location
//float m_pos[N_AXIS]; // machine position in mm
TickType_t xLastWakeTime;
const TickType_t xServoFrequency = SERVO_TIMER_INT_FREQ; // in ticks (typically ms)
uint16_t servo_delay_counter = 0;
float mpos_z, wpos_z;
float z_offset;
xLastWakeTime = xTaskGetTickCount(); // Initialise the xLastWakeTime variable with the current time.
while(true) { // don't ever return from this or the task dies
if (sys.state != STATE_ALARM) { // don't move until alarm is cleared...typically homing
if (!servo_pen_enable ) {
servo_delay_counter++;
servo_pen_enable = (servo_delay_counter > SERVO_TURNON_DELAY);
} else {
mpos_z = system_convert_axis_steps_to_mpos(sys_position, Z_AXIS); // get the machine Z in mm
z_offset = gc_state.coord_system[Z_AXIS]+gc_state.coord_offset[Z_AXIS]; // get the current z work offset
wpos_z = mpos_z - z_offset; // determine the current work Z
calc_pen_servo(wpos_z); // calculate kinematics and move the servos
}
}
vTaskDelayUntil(&xLastWakeTime, xServoFrequency);
}
}
// calculate and set the PWM value for the servo
void calc_pen_servo(float penZ)
{
uint32_t servo_pen_pulse_len;
float servo_pen_pulse_min, servo_pen_pulse_max;
if (!servo_pen_enable) { // only proceed if startup delay as expired
return;
}
if (validate_servo_settings(false)) { // if calibration settings are OK then apply them
if (bit_istrue(settings.dir_invert_mask,bit(Z_AXIS))) { // this allows the user to change the direction via settings
// Apply a calibration to the minimum position
servo_pen_pulse_max = SERVO_MIN_PULSE * (settings.steps_per_mm[Z_AXIS] / 100.0);
// Apply a calibration to the maximum position
servo_pen_pulse_min = SERVO_MAX_PULSE * (settings.max_travel[Z_AXIS] / -100.0);
}
else {
// Apply a calibration to the minimum position
servo_pen_pulse_min = SERVO_MIN_PULSE * (settings.steps_per_mm[Z_AXIS] / 100.0);
// Apply a calibration to the maximum position
servo_pen_pulse_max = SERVO_MAX_PULSE * (settings.max_travel[Z_AXIS] / -100.0);
}
} else { // use the defaults
if (bit_istrue(settings.dir_invert_mask,bit(Z_AXIS))) { // this allows the user to change the direction via settings
servo_pen_pulse_min = SERVO_MAX_PULSE;
servo_pen_pulse_max = SERVO_MIN_PULSE;
}
else {
servo_pen_pulse_min = SERVO_MIN_PULSE;
servo_pen_pulse_max = SERVO_MAX_PULSE;
}
}
// determine the pulse length
servo_pen_pulse_len = (uint32_t)mapConstrain(penZ, SERVO_PEN_RANGE_MIN_MM, SERVO_PEN_RANGE_MAX_MM, servo_pen_pulse_min, servo_pen_pulse_max );
// skip setting value if it is unchanged
if (ledcRead(SERVO_PEN_CHANNEL_NUM) == servo_pen_pulse_len)
return;
// update the PWM value
// ledcWrite appears to have issues with interrupts, so make this a critical section
portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&myMutex);
ledcWrite(SERVO_PEN_CHANNEL_NUM, servo_pen_pulse_len);
portEXIT_CRITICAL(&myMutex);
}
#endif

View File

@ -0,0 +1,76 @@
/*
servo.h
Part of Grbl_ESP32
copyright (c) 2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
To use this, uncomment #define USE_PEN_SERVO in config.h
That should be the only change you need at the top level
Everything occurs as a low priority task that syncs the servo with the
current machine position.
*/
// ==== Begin: Things you are likely to change ====================
//#define SERVO_PEN_PIN GPIO_NUM_27 // FYI...you can disable the Z stepper pins (step & dir)
// the pulse lengths for the min and max travel .. (Note: Servo brands vary)
// If the servo goes backward from what you want, flip the values
// Note: this is not necessarily the servos limits (just the travel you want)
#define SERVO_MIN_PULSE_SEC 0.001 // min pulse in seconds
#define SERVO_MAX_PULSE_SEC 0.002 // max pulse in seconds
// Pulse repeat rate (PWM Frequency)
#define SERVO_PULSE_FREQ 50 // 50Hz ...This is a standard analog servo value. Digital ones can repeat faster
// the range of the servo is constrained
// values above or below these will be limited to the min or max
#define SERVO_PEN_RANGE_MIN_MM 0.0 // the minimum z position in mm
#define SERVO_PEN_RANGE_MAX_MM 5.0 // the minimum z position in mm
// ==== End: Things you are likely to change =======================
// Begin: Advanced settings
#define SERVO_TIMER_NUM 1
#define SERVO_TIMER_INT_FREQ 20 // Hz This is the task frequency
#define SERVO_PEN_CHANNEL_NUM 5
#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
// A way to reduce the turn on current
#define SERVO_TURNON_DELAY SERVO_TIMER_INT_FREQ*3 // Wait this many task counts to turn on servo
#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_CAL_MIN 20.0 // Percent: the minimum allowable calibration value
#define SERVO_CAL_MAX 180.0 // Percent: the maximum allowable calibration value
#ifndef servo_h
#define servo_h
void servo_init();
void servo_disable();
bool validate_servo_settings(bool verbose);
void servoSyncTask(void *pvParameters);
void calc_pen_servo(float penZ);
#endif

View File

@ -0,0 +1,452 @@
/*
settings.c - eeprom configuration handling
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "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); // only the serial could be working at this point
}
}
// 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.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.rpm_max = DEFAULT_SPINDLE_RPM_MAX;
settings.rpm_min = DEFAULT_SPINDLE_RPM_MIN;
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;
}
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.
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data)
{
uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS;
if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) {
// Reset with default zero vector
clear_vector_float(coord_data);
settings_write_coord_data(coord_select,coord_data);
return(false);
}
return(true);
}
// Method to store coord data parameters into EEPROM
void settings_write_coord_data(uint8_t coord_select, float *coord_data)
{
#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
protocol_buffer_synchronize();
#endif
uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS;
memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS);
}
// Method to store build info into EEPROM
// NOTE: This function can only be called in IDLE state.
void settings_store_build_info(char *line)
{
// Build info can only be stored when state is IDLE.
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO,(char*)line, LINE_BUFFER_SIZE);
}
// Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_build_info(char *line)
{
if (!(memcpy_from_eeprom_with_checksum((char*)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) {
// Reset line with default value
line[0] = 0; // Empty line
settings_store_build_info(line);
return(false);
}
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 = value; spindle_init(); break; // Re-initialize spindle rpm calibration
case 31: settings.rpm_min = value; spindle_init(); break; // Re-initialize spindle rpm calibration
case 32:
#ifdef VARIABLE_SPINDLE
if (int_value) { settings.flags |= BITFLAG_LASER_MODE; }
else { settings.flags &= ~BITFLAG_LASER_MODE; }
#else
return(STATUS_SETTING_DISABLED_LASER);
#endif
break;
case 33: settings.spindle_pwm_freq = value; spindle_init(); break; // Re-initialize spindle pwm calibration
case 34: settings.spindle_pwm_off_value = value; spindle_init(); break; // Re-initialize spindle pwm calibration
case 35: settings.spindle_pwm_min_value = value; spindle_init(); break; // Re-initialize spindle pwm calibration
case 36: settings.spindle_pwm_max_value = value; spindle_init(); break; // Re-initialize spindle pwm calibration
case 80:
case 81:
case 82:
case 83:
case 84:
settings.machine_int16[parameter - 80] = int_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.
uint8_t get_step_pin_mask(uint8_t axis_idx)
{
// todo clean this up further up stream
return(1<<axis_idx);
}
// Returns direction pin mask according to Grbl internal axis indexing.
uint8_t get_direction_pin_mask(uint8_t axis_idx)
{
return(1<<axis_idx);
}
// this allows a conditional re-init of the trinamic settings
void settings_spi_driver_init() {
#ifdef USE_TRINAMIC
trinamic_change_settings();
#else
grbl_send(CLIENT_ALL, "[MSG: No SPI drivers setup]\r\n");
#endif
}

View File

@ -0,0 +1,159 @@
/*
settings.h - eeprom configuration handling
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 settings_h
#define settings_h
#include "grbl.h"
// Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl
// when firmware is upgraded. Always stored in byte 0 of eeprom
#define SETTINGS_VERSION 12 // NOTE: Check settings_reset() when moving to next version.
// Define bit flag masks for the boolean settings in settings.flag.
#define BITFLAG_REPORT_INCHES bit(0)
#define BITFLAG_LASER_MODE bit(1)
#define BITFLAG_INVERT_ST_ENABLE bit(2)
#define BITFLAG_HARD_LIMIT_ENABLE bit(3)
#define BITFLAG_HOMING_ENABLE bit(4)
#define BITFLAG_SOFT_LIMIT_ENABLE bit(5)
#define BITFLAG_INVERT_LIMIT_PINS bit(6)
#define BITFLAG_INVERT_PROBE_PIN bit(7)
// Define status reporting boolean enable bit flags in settings.status_report_mask
#define BITFLAG_RT_STATUS_POSITION_TYPE bit(0)
#define BITFLAG_RT_STATUS_BUFFER_STATE bit(1)
// Define settings restore bitflags.
#define SETTINGS_RESTORE_DEFAULTS bit(0)
#define SETTINGS_RESTORE_PARAMETERS bit(1)
#define SETTINGS_RESTORE_STARTUP_LINES bit(2)
#define SETTINGS_RESTORE_BUILD_INFO bit(3)
#define SETTINGS_RESTORE_WIFI_SETTINGS bit(4)
#ifndef SETTINGS_RESTORE_ALL
#define SETTINGS_RESTORE_ALL 0xFF // All bitflags
#endif
// Define EEPROM memory address location values for Grbl settings and parameters
// NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and
// the startup script. The lower half contains the global settings and space for future
// developments.
#define EEPROM_SIZE 1024U
#define EEPROM_ADDR_GLOBAL 1U
#define EEPROM_ADDR_PARAMETERS 512U
#define EEPROM_ADDR_STARTUP_BLOCK 768U
#define EEPROM_ADDR_BUILD_INFO 942U
// Define EEPROM address indexing for coordinate parameters
#define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1)
#define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM+1 // Total number of system stored (from index 0)
// NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59)
#define SETTING_INDEX_G28 N_COORDINATE_SYSTEM // Home position 1
#define SETTING_INDEX_G30 N_COORDINATE_SYSTEM+1 // Home position 2
// #define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset (G92.2,G92.3 not supported)
// Define Grbl axis settings numbering scheme. Starts at START_VAL, every INCREMENT, over N_SETTINGS.
#ifndef SHOW_EXTENDED_SETTINGS
#define AXIS_N_SETTINGS 4
#else
#define AXIS_N_SETTINGS 8
#endif
#define AXIS_SETTINGS_START_VAL 100 // NOTE: Reserving settings values >= 100 for axis settings. Up to 255.
#define AXIS_SETTINGS_INCREMENT 10 // Must be greater than the number of axis settings
#define USER_SETTING_COUNT 5 // for user to define for their machine
// Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards)
typedef struct {
// Axis settings
float steps_per_mm[N_AXIS];
float max_rate[N_AXIS];
float acceleration[N_AXIS];
float max_travel[N_AXIS];
float current[N_AXIS]; // $140... run current (extended set)
float hold_current[N_AXIS]; // $150 percent of run current (extended set)
uint16_t microsteps[N_AXIS]; // $160... (extended set)
uint8_t stallguard[N_AXIS]; // $170... (extended set)
// Remaining Grbl settings
uint8_t pulse_microseconds;
uint8_t step_invert_mask;
uint8_t dir_invert_mask;
uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable.
uint8_t status_report_mask; // Mask to indicate desired report data.
float junction_deviation;
float arc_tolerance;
float spindle_pwm_freq; // $33 Hz (extended set)
float spindle_pwm_off_value; // $34 Percent (extended set)
float spindle_pwm_min_value; // $35 Percent (extended set)
float spindle_pwm_max_value; // $36 Percent (extended set)
float rpm_max;
float rpm_min;
uint8_t flags; // Contains default boolean settings
uint8_t homing_dir_mask;
float homing_feed_rate;
float homing_seek_rate;
uint16_t homing_debounce_delay;
float homing_pulloff;
int16_t machine_int16[USER_SETTING_COUNT]; // settings starting at 80 to be defined by the user
float machine_float[USER_SETTING_COUNT]; // settings starting at 80 to be defined by the user
} settings_t;
extern settings_t settings;
// Initialize the configuration subsystem (load settings from EEPROM)
void settings_init();
void settings_restore(uint8_t restore_flag);
void write_global_settings();
uint8_t read_global_settings();
uint8_t settings_read_startup_line(uint8_t n, char *line);
void settings_store_startup_line(uint8_t n, char *line);
uint8_t settings_read_build_info(char *line);
void settings_store_build_info(char *line);
uint8_t settings_store_global_setting(uint8_t parameter, float value);
// Writes selected coordinate data to EEPROM
void settings_write_coord_data(uint8_t coord_select, float *coord_data);
// Reads selected coordinate data from EEPROM
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data);
// Returns the step pin mask according to Grbl's internal axis numbering
uint8_t get_step_pin_mask(uint8_t i);
// Returns the direction pin mask according to Grbl's internal axis numbering
uint8_t get_direction_pin_mask(uint8_t i);
void settings_spi_driver_init();
#endif

View File

@ -0,0 +1,123 @@
/*
solenoid_pen.cpp
Part of Grbl_ESP32
copyright (c) 2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
#ifdef USE_PEN_SOLENOID
static TaskHandle_t solenoidSyncTaskHandle = 0;
// used to delay turn on
bool solenoid_pen_enable;
uint16_t solenoide_hold_count;
void solenoid_init()
{
grbl_send(CLIENT_SERIAL, "[MSG:Solenoid Mode]\r\n"); // startup message
//validate_servo_settings(true); // display any calibration errors
solenoid_pen_enable = false; // start delay has not completed yet.
solenoide_hold_count = 0; // initialize
// setup PWM channel
ledcSetup(SOLENOID_CHANNEL_NUM, SOLENOID_PWM_FREQ, SOLENOID_PWM_RES_BITS);
ledcAttachPin(SOLENOID_PEN_PIN, SOLENOID_CHANNEL_NUM);
solenoid_disable(); // start it it off
// setup a task that will calculate the determine and set the servo position
xTaskCreatePinnedToCore( solenoidSyncTask, // task
"solenoidSyncTask", // name for task
4096, // size of task stack
NULL, // parameters
1, // priority
&solenoidSyncTaskHandle,
0 // core
);
}
// turn off the PWM (0 duty)
void solenoid_disable()
{
ledcWrite(SOLENOID_CHANNEL_NUM, 0);
}
// this is the task
void solenoidSyncTask(void *pvParameters)
{
int32_t current_position[N_AXIS]; // copy of current location
float m_pos[N_AXIS]; // machine position in mm
TickType_t xLastWakeTime;
const TickType_t xSolenoidFrequency = SOLENOID_TIMER_INT_FREQ; // in ticks (typically ms)
uint16_t solenoid_delay_counter = 0;
xLastWakeTime = xTaskGetTickCount(); // Initialise the xLastWakeTime variable with the current time.
while(true) { // don't ever return from this or the task dies
if (!solenoid_pen_enable) {
solenoid_delay_counter++;
solenoid_pen_enable = (solenoid_delay_counter > SOLENOID_TURNON_DELAY);
}
else {
memcpy(current_position,sys_position,sizeof(sys_position)); // get current position in step
system_convert_array_steps_to_mpos(m_pos,current_position); // convert to millimeters
calc_solenoid(m_pos[Z_AXIS]); // calculate kinematics and move the servos
}
vTaskDelayUntil(&xLastWakeTime, xSolenoidFrequency);
}
}
// calculate and set the PWM value for the servo
void calc_solenoid(float penZ)
{
uint32_t solenoid_pen_pulse_len;
if (!solenoid_pen_enable) // only proceed if startup delay as expired
return;
if (penZ < 0 && (sys.state != STATE_ALARM)) { // alarm also makes it go up
solenoide_hold_count = 0; // reset this count
solenoid_pen_pulse_len = 0; //
}
else {
if (solenoide_hold_count < SOLENOID_PULSE_LEN_HOLD) {
solenoid_pen_pulse_len = SOLENOID_PULSE_LEN_UP;
solenoide_hold_count++;
}
else {
solenoid_pen_pulse_len = SOLENOID_PULSE_LEN_HOLD;
}
}
// skip setting value if it is unchanged
if (ledcRead(SOLENOID_CHANNEL_NUM) == solenoid_pen_pulse_len)
return;
// update the PWM value
// ledcWrite appears to have issues with interrupts, so make this a critical section
portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&myMutex);
ledcWrite(SOLENOID_CHANNEL_NUM, solenoid_pen_pulse_len);
portEXIT_CRITICAL(&myMutex);
}
#endif

View File

@ -0,0 +1,53 @@
/*
solenoid_pen.h
Part of Grbl_ESP32
copyright (c) 2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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/>.
Usage notes:
This is designed to use a solenoid to lift a pen.
When the current Z location is below zero the pen is down
If the Z goes to zero or above the pen goes up.
There are two power levels, the initial pull up strength, then the hold strength
Note: There is a still a virtual Z axis that has a finite speed.
If your gcode is commanding long travels in Z, there will be delays
between solenoid states as the Z "travels" to the location that will
change the state.
*/
#define SOLENOID_PWM_FREQ 5000
#define SOLENOID_PWM_RES_BITS 8
#define SOLENOID_TURNON_DELAY (SOLENOID_TIMER_INT_FREQ/2)
#define SOLENOID_PULSE_LEN_UP 255
#define SOLENOID_HOLD_DELAY (SOLENOID_TIMER_INT_FREQ/2) // in task counts...after this delay power will change to hold level
#define SOLENOID_PULSE_LEN_HOLD 80 // solenoid hold level ... typically a lower value to prevent overheating
#define SOLENOID_TIMER_INT_FREQ 50
#ifndef solenoid_h
#define solenoid_h
void solenoid_init();
void solenoid_disable();
void solenoidSyncTask(void *pvParameters);
void calc_solenoid(float penZ);
#endif

View File

@ -0,0 +1,257 @@
/*
spindle_control.cpp - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
#ifdef SPINDLE_PWM_PIN
static float pwm_gradient; // Precalulated value to speed up rpm to PWM conversions.
float spindle_pwm_period;
float spindle_pwm_off_value;
float spindle_pwm_min_value;
float spindle_pwm_max_value;
#endif
void spindle_init()
{
#ifdef SPINDLE_PWM_PIN
#ifdef INVERT_SPINDLE_PWM
grbl_send(CLIENT_SERIAL, "[MSG: INVERT_SPINDLE_PWM]\r\n");
#endif
#ifdef INVERT_SPINDLE_ENABLE_PIN
grbl_send(CLIENT_SERIAL, "[MSG: INVERT_SPINDLE_ENABLE_PIN]\r\n");
#endif
spindle_pwm_period = SPINDLE_PULSE_RES_COUNT;
spindle_pwm_off_value = (spindle_pwm_period * settings.spindle_pwm_off_value / 100);
spindle_pwm_min_value = (spindle_pwm_period * settings.spindle_pwm_min_value / 100);
spindle_pwm_max_value = (spindle_pwm_period * settings.spindle_pwm_max_value / 100);
//pwm_gradient = (settings.spindle_pwm_max_value - settings.spindle_pwm_min_value)/(settings.rpm_max-settings.rpm_min);
pwm_gradient = (spindle_pwm_max_value-spindle_pwm_min_value)/(settings.rpm_max-settings.rpm_min);
if ( (F_TIMERS / (uint32_t)settings.spindle_pwm_freq) < spindle_pwm_max_value) {
/*
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 = 13bits (8192)
*/
grbl_sendf(CLIENT_SERIAL, "[MSG: Warning! Spindle freq %5.0f too high for requested PWM max %5.2f%% (%5.0f)]\r\n", settings.spindle_pwm_freq, settings.spindle_pwm_max_value, spindle_pwm_max_value);
}
// Use DIR and Enable if pins are defined
#ifdef SPINDLE_ENABLE_PIN
pinMode(SPINDLE_ENABLE_PIN, OUTPUT);
#endif
#ifdef SPINDLE_DIR_PIN
pinMode(SPINDLE_DIR_PIN, OUTPUT);
#endif
// use the LED control feature to setup PWM https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/ledc.html
ledcSetup(SPINDLE_PWM_CHANNEL, (double)settings.spindle_pwm_freq, SPINDLE_PWM_BIT_PRECISION); // setup the channel
ledcAttachPin(SPINDLE_PWM_PIN, SPINDLE_PWM_CHANNEL); // attach the PWM to the pin
// Start with spindle off off
spindle_stop();
#endif
}
void spindle_stop()
{
spindle_set_enable(false);
#ifdef SPINDLE_PWM_PIN
#ifndef INVERT_SPINDLE_PWM
grbl_analogWrite(SPINDLE_PWM_CHANNEL, spindle_pwm_off_value);
#else
grbl_analogWrite(SPINDLE_PWM_CHANNEL, (1<<SPINDLE_PWM_BIT_PRECISION)); // TO DO...wrong for min_pwm
#endif
#endif
}
uint8_t spindle_get_state() // returns SPINDLE_STATE_DISABLE, SPINDLE_STATE_CW or SPINDLE_STATE_CCW
{
// TODO Update this when direction and enable pin are added
#ifndef SPINDLE_PWM_PIN
return(SPINDLE_STATE_DISABLE);
#else
if (ledcRead(SPINDLE_PWM_CHANNEL) == 0) // Check the PWM value
return(SPINDLE_STATE_DISABLE);
else
{
#ifdef SPINDLE_DIR_PIN
if (digitalRead(SPINDLE_DIR_PIN))
return (SPINDLE_STATE_CW);
else
return(SPINDLE_STATE_CCW);
#else
return(SPINDLE_STATE_CW);
#endif
}
#endif
}
void spindle_set_speed(uint32_t pwm_value)
{
#ifndef SPINDLE_PWM_PIN
return;
#else
#ifndef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED
spindle_set_enable(true);
#else
spindle_set_enable(pwm_value != 0);
#endif
#ifndef INVERT_SPINDLE_PWM
grbl_analogWrite(SPINDLE_PWM_CHANNEL, pwm_value);
#else
grbl_analogWrite(SPINDLE_PWM_CHANNEL, (1<<SPINDLE_PWM_BIT_PRECISION) - pwm_value);
#endif
#endif
}
uint32_t spindle_compute_pwm_value(float rpm){
#ifdef SPINDLE_PWM_PIN
uint32_t pwm_value;
rpm *= (0.010*sys.spindle_speed_ovr); // Scale by spindle speed override value.
// Calculate PWM register value based on rpm max/min settings and programmed rpm.
if ((settings.rpm_min >= settings.rpm_max) || (rpm >= settings.rpm_max)) {
// No PWM range possible. Set simple on/off spindle control pin state.
sys.spindle_speed = settings.rpm_max;
pwm_value = spindle_pwm_max_value;
} else if (rpm <= settings.rpm_min) {
if (rpm == 0.0) { // S0 disables spindle
sys.spindle_speed = 0.0;
pwm_value = spindle_pwm_off_value;
} else { // Set minimum PWM output
sys.spindle_speed = settings.rpm_min;
pwm_value = spindle_pwm_min_value;
}
} else {
// Compute intermediate PWM value with linear spindle speed model.
// NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight.
sys.spindle_speed = rpm;
#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE
pwm_value = piecewise_linear_fit(rpm);
#else
pwm_value = floor((rpm - settings.rpm_min)*pwm_gradient) + settings.spindle_pwm_min_value;
#endif
}
return(pwm_value);
#else
return(0); // no SPINDLE_PWM_PIN
#endif
}
// Called by spindle_set_state() and step segment generator. Keep routine small and efficient.
void spindle_set_state(uint8_t state, float rpm)
{
#ifdef SPINDLE_PWM_PIN
if (sys.abort) { return; } // Block during abort.
if (state == SPINDLE_DISABLE) { // Halt or set spindle direction and rpm.
sys.spindle_speed = 0.0;
spindle_stop();
} else {
// TODO ESP32 Enable and direction control
#ifdef SPINDLE_DIR_PIN
digitalWrite(SPINDLE_DIR_PIN, state == SPINDLE_ENABLE_CW);
#endif
// NOTE: Assumes all calls to this function is when Grbl is not moving or must remain off.
if (settings.flags & BITFLAG_LASER_MODE) {
if (state == SPINDLE_ENABLE_CCW) { rpm = 0.0; } // TODO: May need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE);
}
spindle_set_speed(spindle_compute_pwm_value(rpm));
}
sys.report_ovr_counter = 0; // Set to report change immediately
#endif
}
void spindle_sync(uint8_t state, float rpm)
{
if (sys.state == STATE_CHECK_MODE) {
return;
}
protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed.
spindle_set_state(state,rpm);
}
void grbl_analogWrite(uint8_t chan, uint32_t duty)
{
if (ledcRead(chan) != duty) // reduce unnecessary calls to ledcWrite()
{
// Useful for debug, but too many messages in laser mode
// grbl_sendf(CLIENT_SERIAL, "[MSG: grbl_analogWrite %d]\r\n", duty);
ledcWrite(chan, duty);
}
}
void spindle_set_enable(bool enable)
{
#ifdef SPINDLE_ENABLE_PIN
#ifndef INVERT_SPINDLE_ENABLE_PIN
digitalWrite(SPINDLE_ENABLE_PIN, enable); // turn off (low) with zero speed
#else
digitalWrite(SPINDLE_ENABLE_PIN, !enable); // turn off (high) with zero speed
#endif
#endif
}
uint32_t piecewise_linear_fit(float rpm) {
uint32_t pwm_value;
#if (N_PIECES > 3)
if (rpm > RPM_POINT34) {
pwm_value = floor(RPM_LINE_A4*rpm - RPM_LINE_B4);
} else
#endif
#if (N_PIECES > 2)
if (rpm > RPM_POINT23) {
pwm_value = floor(RPM_LINE_A3*rpm - RPM_LINE_B3);
} else
#endif
#if (N_PIECES > 1)
if (rpm > RPM_POINT12) {
pwm_value = floor(RPM_LINE_A2*rpm - RPM_LINE_B2);
} else
#endif
{
pwm_value = floor(RPM_LINE_A1*rpm - RPM_LINE_B1);
}
return pwm_value;
}

View File

@ -0,0 +1,47 @@
/*
spindle.h - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 spindle_control_h
#define spindle_control_h
#include "grbl.h"
#define SPINDLE_NO_SYNC false
#define SPINDLE_FORCE_SYNC true
#define SPINDLE_STATE_DISABLE 0 // Must be zero.
#define SPINDLE_STATE_CW bit(0)
#define SPINDLE_STATE_CCW bit(1)
#define SPINDLE_PULSE_RES_COUNT ((1<<SPINDLE_PWM_BIT_PRECISION) -1) //(don't change)
void spindle_init();
void spindle_stop();
uint8_t spindle_get_state();
void spindle_set_speed(uint32_t pwm_value);
uint32_t spindle_compute_pwm_value(float rpm);
void spindle_set_state(uint8_t state, float rpm);
void spindle_sync(uint8_t state, float rpm);
void grbl_analogWrite(uint8_t chan, uint32_t duty);
void spindle_set_enable(bool enable);
uint32_t piecewise_linear_fit(float rpm);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,131 @@
/*
stepper.h - stepper motor driver: executes motion plans of planner.c using the stepper motors
Part of Grbl
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 stepper_h
#define stepper_h
#ifndef SEGMENT_BUFFER_SIZE
#define SEGMENT_BUFFER_SIZE 6
#endif
#include "grbl.h"
#include "config.h"
// Some useful constants.
#define DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) // min/segment
#define REQ_MM_INCREMENT_SCALAR 1.25
#define RAMP_ACCEL 0
#define RAMP_CRUISE 1
#define RAMP_DECEL 2
#define RAMP_DECEL_OVERRIDE 3
#define PREP_FLAG_RECALCULATE bit(0)
#define PREP_FLAG_HOLD_PARTIAL_BLOCK bit(1)
#define PREP_FLAG_PARKING bit(2)
#define PREP_FLAG_DECEL_OVERRIDE bit(3)
// Define Adaptive Multi-Axis Step-Smoothing(AMASS) levels and cutoff frequencies. The highest level
// frequency bin starts at 0Hz and ends at its cutoff frequency. The next lower level frequency bin
// starts at the next higher cutoff frequency, and so on. The cutoff frequencies for each level must
// be considered carefully against how much it over-drives the stepper ISR, the accuracy of the 16-bit
// timer, and the CPU overhead. Level 0 (no AMASS, normal operation) frequency bin starts at the
// Level 1 cutoff frequency and up to as fast as the CPU allows (over 30kHz in limited testing).
// NOTE: AMASS cutoff frequency multiplied by ISR overdrive factor must not exceed maximum step frequency.
// NOTE: Current settings are set to overdrive the ISR to no more than 16kHz, balancing CPU overhead
// and timer accuracy. Do not alter these settings unless you know what you are doing.
///#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
#define MAX_AMASS_LEVEL 3
// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency.
// Note ESP32 use F_STEPPER_TIMER rather than the AVR F_CPU
#define AMASS_LEVEL1 (F_STEPPER_TIMER/8000) // Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz)
#define AMASS_LEVEL2 (F_STEPPER_TIMER/4000) // Over-drives ISR (x4)
#define AMASS_LEVEL3 (F_STEPPER_TIMER/2000) // Over-drives ISR (x8)
#if MAX_AMASS_LEVEL <= 0
error "AMASS must have 1 or more levels to operate correctly."
#endif
//#endif
#define STEP_TIMER_GROUP TIMER_GROUP_0
#define STEP_TIMER_INDEX TIMER_0
// esp32 work around for diable in main loop
extern uint64_t stepper_idle_counter;
extern bool stepper_idle;
extern uint8_t ganged_mode;
// -- Task handles for use in the notifications
void IRAM_ATTR onSteppertimer();
void IRAM_ATTR onStepperOffTimer();
#ifdef USE_RMT_STEPS
void initRMT();
#endif
void stepper_init();
// Enable steppers, but cycle does not start unless called by motion control or realtime command.
void st_wake_up();
// Immediately disables steppers
void st_go_idle();
// Generate the step and direction port invert masks.
void st_generate_step_dir_invert_masks();
// Reset the stepper subsystem variables
void st_reset();
// Changes the run state of the step segment buffer to execute the special parking motion.
void st_parking_setup_buffer();
// Restores the step segment buffer to the normal run state after a parking motion.
void st_parking_restore_buffer();
// Reloads step segment buffer. Called continuously by realtime execution system.
void st_prep_buffer();
// Called by planner_recalculate() when the executing block is updated by the new plan.
void st_update_plan_block_parameters();
// Called by realtime status reporting if realtime rate reporting is enabled in config.h.
float st_get_realtime_rate();
// disable (or enable) steppers via STEPPERS_DISABLE_PIN
void set_stepper_disable(uint8_t disable);
bool get_stepper_disable(); // returns the state of the pin
void set_step_pin_on(uint8_t axis, uint8_t isOn);
void set_direction_pin_on(uint8_t axis, uint8_t isOn);
void set_stepper_pins_on(uint8_t onMask);
void set_direction_pins_on(uint8_t onMask);
void Stepper_Timer_WritePeriod(uint64_t alarm_val);
void Stepper_Timer_Start();
void Stepper_Timer_Stop();
#endif

View File

@ -0,0 +1,614 @@
/*
system.cpp - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modified for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 "grbl.h"
#include "config.h"
xQueueHandle control_sw_queue; // used by control switch debouncing
bool debouncing = false; // debouncing in process
void system_ini() // Renamed from system_init() due to conflict with esp32 files
{
// setup control inputs
#ifndef IGNORE_CONTROL_PINS
#ifdef CONTROL_SAFETY_DOOR_PIN
pinMode(CONTROL_SAFETY_DOOR_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CONTROL_SAFETY_DOOR_PIN), isr_control_inputs, CHANGE);
#endif
#ifdef CONTROL_RESET_PIN
pinMode(CONTROL_RESET_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CONTROL_RESET_PIN), isr_control_inputs, CHANGE);
#endif
#ifdef CONTROL_FEED_HOLD_PIN
pinMode(CONTROL_FEED_HOLD_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CONTROL_FEED_HOLD_PIN), isr_control_inputs, CHANGE);
#endif
#ifdef CONTROL_CYCLE_START_PIN
pinMode(CONTROL_CYCLE_START_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(CONTROL_CYCLE_START_PIN), isr_control_inputs, CHANGE);
#endif
#ifdef MACRO_BUTTON_0_PIN
grbl_send(CLIENT_SERIAL, "[MSG:Macro Pin 0]\r\n");
pinMode(MACRO_BUTTON_0_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(MACRO_BUTTON_0_PIN), isr_control_inputs, CHANGE);
#endif
#ifdef MACRO_BUTTON_1_PIN
grbl_send(CLIENT_SERIAL, "[MSG:Macro Pin 1]\r\n");
pinMode(MACRO_BUTTON_1_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(MACRO_BUTTON_1_PIN), isr_control_inputs, CHANGE);
#endif
#ifdef MACRO_BUTTON_2_PIN
grbl_send(CLIENT_SERIAL, "[MSG:Macro Pin 2]\r\n");
pinMode(MACRO_BUTTON_2_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(MACRO_BUTTON_2_PIN), isr_control_inputs, CHANGE);
#endif
#ifdef MACRO_BUTTON_3_PIN
grbl_send(CLIENT_SERIAL, "[MSG:Macro Pin 3]\r\n");
pinMode(MACRO_BUTTON_3_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(MACRO_BUTTON_3_PIN), isr_control_inputs, CHANGE);
#endif
#ifdef ENABLE_CONTROL_SW_DEBOUNCE
// setup task used for debouncing
control_sw_queue = xQueueCreate(10, sizeof( int ));
xTaskCreate(controlCheckTask,
"controlCheckTask",
2048,
NULL,
5, // priority
NULL);
#endif
#endif
//customize pin definition if needed
#if (GRBL_SPI_SS != -1) || (GRBL_SPI_MISO != -1) || (GRBL_SPI_MOSI != -1) || (GRBL_SPI_SCK != -1)
SPI.begin(GRBL_SPI_SCK, GRBL_SPI_MISO, GRBL_SPI_MOSI, GRBL_SPI_SS);
#endif
// Setup USER_DIGITAL_PINs controlled by M62 and M63
#ifdef USER_DIGITAL_PIN_1
pinMode(USER_DIGITAL_PIN_1, OUTPUT);
sys_io_control(1<<1, false); // turn off
#endif
#ifdef USER_DIGITAL_PIN_2
pinMode(USER_DIGITAL_PIN_2, OUTPUT);
sys_io_control(1<<2, false); // turn off
#endif
#ifdef USER_DIGITAL_PIN_3
pinMode(USER_DIGITAL_PIN_3, OUTPUT);
sys_io_control(1<<3, false); // turn off
#endif
#ifdef USER_DIGITAL_PIN_4
pinMode(USER_DIGITAL_PIN_4, OUTPUT);
sys_io_control(1<<4, false); // turn off
#endif
}
#ifdef ENABLE_CONTROL_SW_DEBOUNCE
// this is the debounce task
void controlCheckTask(void *pvParameters)
{
while(true) {
int evt;
xQueueReceive(control_sw_queue, &evt, portMAX_DELAY); // block until receive queue
vTaskDelay(CONTROL_SW_DEBOUNCE_PERIOD); // delay a while
uint8_t pin = system_control_get_state();
if (pin) {
system_exec_control_pin(pin);
}
debouncing = false;
}
}
#endif
void IRAM_ATTR isr_control_inputs()
{
#ifdef ENABLE_CONTROL_SW_DEBOUNCE
// we will start a task that will recheck the switches after a small delay
int evt;
if (!debouncing) { // prevent resending until debounce is done
debouncing = true;
xQueueSendFromISR(control_sw_queue, &evt, NULL);
}
#else
uint8_t pin = system_control_get_state();
system_exec_control_pin(pin);
#endif
}
// Executes user startup script, if stored.
void system_execute_startup(char *line)
{
uint8_t n;
for (n=0; n < N_STARTUP_LINE; n++) {
if (!(settings_read_startup_line(n, line))) {
line[0] = 0;
report_execute_startup_message(line,STATUS_SETTING_READ_FAIL, CLIENT_SERIAL);
} else {
if (line[0] != 0) {
uint8_t status_code = gc_execute_line(line, CLIENT_SERIAL);
report_execute_startup_message(line,status_code, CLIENT_SERIAL);
}
}
}
}
// Directs and executes one line of formatted input from protocol_process. While mostly
// incoming streaming g-code blocks, this also executes Grbl internal commands, such as
// settings, initiating the homing cycle, and toggling switch states. This differs from
// the realtime command module by being susceptible to when Grbl is ready to execute the
// next line during a cycle, so for switches like block delete, the switch only effects
// the lines that are processed afterward, not necessarily real-time during a cycle,
// since there are motions already stored in the buffer. However, this 'lag' should not
// be an issue, since these commands are not typically used during a cycle.
uint8_t system_execute_line(char *line, uint8_t client)
{
uint8_t char_counter = 1;
uint8_t helper_var = 0; // Helper variable
float parameter, value;
switch( line[char_counter] ) {
case 0 : report_grbl_help(client); break;
case 'J' : // Jogging
// Execute only if in IDLE or JOG states.
if (sys.state != STATE_IDLE && sys.state != STATE_JOG) { return(STATUS_IDLE_ERROR); }
if(line[2] != '=') { return(STATUS_INVALID_STATEMENT); }
return(gc_execute_line(line, client)); // NOTE: $J= is ignored inside g-code parser and used to detect jog motions.
break;
case '$': case 'G': case 'C': case 'X':
if ( line[2] != 0 ) { return(STATUS_INVALID_STATEMENT); }
switch( line[1] ) {
case '$' : // Prints Grbl settings
if ( sys.state & (STATE_CYCLE | STATE_HOLD) ) { return(STATUS_IDLE_ERROR); } // Block during cycle. Takes too long to print.
else { report_grbl_settings(client); }
break;
case 'G' : // Prints gcode parser state
// TODO: Move this to realtime commands for GUIs to request this data during suspend-state.
report_gcode_modes(client);
break;
case 'C' : // Set check g-code mode [IDLE/CHECK]
// 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);
}
break;
case 'X' : // Disable alarm lock [ALARM]
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.
break;
}
break;
default :
// Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing)
if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); }
switch( line[1] ) {
case '#' : // Print Grbl NGC parameters
if ( line[2] != 0 ) { return(STATUS_INVALID_STATEMENT); }
else { report_ngc_parameters(client); }
break;
case 'H' : // Perform homing cycle [IDLE/ALARM] $H
if (bit_isfalse(settings.flags,BITFLAG_HOMING_ENABLE)) {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
if (line[2] == 0) {
mc_homing_cycle(HOMING_CYCLE_ALL);
#ifdef HOMING_SINGLE_AXIS_COMMANDS
} else if (line[3] == 0) {
switch (line[2]) {
case 'X': mc_homing_cycle(HOMING_CYCLE_X); break;
case 'Y': mc_homing_cycle(HOMING_CYCLE_Y); break;
case 'Z': mc_homing_cycle(HOMING_CYCLE_Z); break;
case 'A': mc_homing_cycle(HOMING_CYCLE_A); break;
case 'B': mc_homing_cycle(HOMING_CYCLE_B); break;
case 'C': mc_homing_cycle(HOMING_CYCLE_C); break;
default: return(STATUS_INVALID_STATEMENT);
}
#endif
} else { return(STATUS_INVALID_STATEMENT); }
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 (line[2] == 0) { system_execute_startup(line); }
}
break;
case 'S' : // Puts Grbl to sleep [IDLE/ALARM]
if ((line[2] != 'L') || (line[3] != 'P') || (line[4] != 0)) { return(STATUS_INVALID_STATEMENT); }
system_set_exec_state_flag(EXEC_SLEEP); // Set to execute sleep mode immediately
break;
case 'I' : // Print or store build info. [IDLE/ALARM]
if ( line[++char_counter] == 0 ) {
settings_read_build_info(line);
report_build_info(line, client);
#ifdef ENABLE_BUILD_INFO_WRITE_COMMAND
} else { // Store startup line [IDLE/ALARM]
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
helper_var = char_counter; // Set helper variable as counter to start of user info line.
do {
line[char_counter-helper_var] = line[char_counter];
} while (line[char_counter++] != 0);
settings_store_build_info(line);
#endif
}
break;
case 'R' : // Restore defaults [IDLE/ALARM]
if ((line[2] != 'S') || (line[3] != 'T') || (line[4] != '=') || (line[6] != 0)) { return(STATUS_INVALID_STATEMENT); }
switch (line[5]) {
#ifdef ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS
case '$': settings_restore(SETTINGS_RESTORE_DEFAULTS); break;
#endif
#ifdef ENABLE_RESTORE_EEPROM_CLEAR_PARAMETERS
case '#': settings_restore(SETTINGS_RESTORE_PARAMETERS); break;
#endif
#ifdef ENABLE_RESTORE_EEPROM_WIPE_ALL
case '*': settings_restore(SETTINGS_RESTORE_ALL); break;
#endif
#if defined(ENABLE_BLUETOOTH) || defined(ENABLE_WIFI)
case '@': settings_restore(SETTINGS_RESTORE_WIFI_SETTINGS); break;
#endif
default: return(STATUS_INVALID_STATEMENT);
}
report_feedback_message(MESSAGE_RESTORE_DEFAULTS);
mc_reset(); // Force reset to ensure settings are initialized correctly.
break;
case 'N' : // Startup lines. [IDLE/ALARM]
if ( line[++char_counter] == 0 ) { // Print startup lines
for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) {
if (!(settings_read_startup_line(helper_var, line))) {
report_status_message(STATUS_SETTING_READ_FAIL, CLIENT_SERIAL);
} else {
report_startup_line(helper_var,line, client);
}
}
break;
} else { // Store startup line [IDLE Only] Prevents motion during ALARM.
if (sys.state != STATE_IDLE) { return(STATUS_IDLE_ERROR); } // Store only when idle.
helper_var = true; // Set helper_var to flag storing method.
// No break. Continues into default: to read remaining command characters.
}
default : // Storing setting methods [IDLE/ALARM]
if(!read_float(line, &char_counter, &parameter)) { return(STATUS_BAD_NUMBER_FORMAT); }
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
if (helper_var) { // Store startup line
// Prepare sending gcode block to gcode parser by shifting all characters
helper_var = char_counter; // Set helper variable as counter to start of gcode block
do {
line[char_counter-helper_var] = line[char_counter];
} while (line[char_counter++] != 0);
// Execute gcode block to ensure block is valid.
helper_var = gc_execute_line(line, CLIENT_SERIAL); // Set helper_var to returned status code.
if (helper_var) { return(helper_var); }
else {
helper_var = trunc(parameter); // Set helper_var to int value of parameter
settings_store_startup_line(helper_var,line);
}
} else { // Store global setting.
if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); }
if((line[char_counter] != 0) || (parameter > 255)) { return(STATUS_INVALID_STATEMENT); }
return(settings_store_global_setting((uint8_t)parameter, value));
}
}
}
return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
}
// Returns if safety door is ajar(T) or closed(F), based on pin state.
uint8_t system_check_safety_door_ajar()
{
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
return(system_control_get_state() & CONTROL_PIN_INDEX_SAFETY_DOOR);
#else
return(false); // Input pin not enabled, so just return that it's closed.
#endif
}
// Special handlers for setting and clearing Grbl's real-time execution flags.
void system_set_exec_state_flag(uint8_t mask) {
// TODO uint8_t sreg = SREG;
// TODO cli();
sys_rt_exec_state |= (mask);
// TODO SREG = sreg;
}
void system_clear_exec_state_flag(uint8_t mask) {
//uint8_t sreg = SREG;
//cli();
sys_rt_exec_state &= ~(mask);
//SREG = sreg;
}
void system_set_exec_alarm(uint8_t code) {
//uint8_t sreg = SREG;
//cli();
sys_rt_exec_alarm = code;
//SREG = sreg;
}
void system_clear_exec_alarm() {
//uint8_t sreg = SREG;
//cli();
sys_rt_exec_alarm = 0;
//SREG = sreg;
}
void system_set_exec_motion_override_flag(uint8_t mask) {
//uint8_t sreg = SREG;
//cli();
sys_rt_exec_motion_override |= (mask);
//SREG = sreg;
}
void system_set_exec_accessory_override_flag(uint8_t mask) {
//uint8_t sreg = SREG;
//cli();
sys_rt_exec_accessory_override |= (mask);
//SREG = sreg;
}
void system_clear_exec_motion_overrides() {
//uint8_t sreg = SREG;
//cli();
sys_rt_exec_motion_override = 0;
//SREG = sreg;
}
void system_clear_exec_accessory_overrides() {
//uint8_t sreg = SREG;
//cli();
sys_rt_exec_accessory_override = 0;
//SREG = sreg;
}
void system_flag_wco_change()
{
#ifdef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE
protocol_buffer_synchronize();
#endif
sys.report_wco_counter = 0;
}
// Returns machine position of axis 'idx'. Must be sent a 'step' array.
// NOTE: If motor steps and machine position are not in the same coordinate frame, this function
// serves as a central place to compute the transformation.
float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx)
{
float pos;
#ifdef COREXY
if (idx==X_AXIS) {
pos = (float)system_convert_corexy_to_x_axis_steps(steps) / settings.steps_per_mm[idx];
} else if (idx==Y_AXIS) {
pos = (float)system_convert_corexy_to_y_axis_steps(steps) / settings.steps_per_mm[idx];
} else {
pos = steps[idx]/settings.steps_per_mm[idx];
}
#else
pos = steps[idx]/settings.steps_per_mm[idx];
#endif
return(pos);
}
void system_convert_array_steps_to_mpos(float *position, int32_t *steps)
{
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
position[idx] = system_convert_axis_steps_to_mpos(steps, idx);
}
return;
}
// Checks and reports if target array exceeds machine travel limits.
uint8_t system_check_travel_limits(float *target)
{
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
#ifdef HOMING_FORCE_SET_ORIGIN
// When homing forced set origin is enabled, soft limits checks need to account for directionality.
// NOTE: max_travel is stored as negative
if (bit_istrue(settings.homing_dir_mask,bit(idx))) {
if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) { return(true); }
} else {
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); }
}
#else
// NOTE: max_travel is stored as negative
#ifdef HOMING_FORCE_POSITIVE_SPACE
if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) { return(true); }
#else
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); }
#endif
#endif
}
return(false);
}
// Returns control pin state as a uint8 bitfield. Each bit indicates the input pin state, where
// triggered is 1 and not triggered is 0. Invert mask is applied. Bitfield organization is
// defined by the CONTROL_PIN_INDEX in the header file.
uint8_t system_control_get_state()
{
uint8_t defined_pin_mask = 0; // a mask of defined pins
#ifdef IGNORE_CONTROL_PINS
return 0;
#endif
uint8_t control_state = 0;
#ifdef CONTROL_SAFETY_DOOR_PIN
defined_pin_mask |= CONTROL_PIN_INDEX_SAFETY_DOOR;
if (digitalRead(CONTROL_SAFETY_DOOR_PIN)) { control_state |= CONTROL_PIN_INDEX_SAFETY_DOOR; }
#endif
#ifdef CONTROL_RESET_PIN
defined_pin_mask |= CONTROL_PIN_INDEX_RESET;
if (digitalRead(CONTROL_RESET_PIN)) { control_state |= CONTROL_PIN_INDEX_RESET; }
#endif
#ifdef CONTROL_FEED_HOLD_PIN
defined_pin_mask |= CONTROL_PIN_INDEX_FEED_HOLD;
if (digitalRead(CONTROL_FEED_HOLD_PIN)) { control_state |= CONTROL_PIN_INDEX_FEED_HOLD; }
#endif
#ifdef CONTROL_CYCLE_START_PIN
defined_pin_mask |= CONTROL_PIN_INDEX_CYCLE_START;
if (digitalRead(CONTROL_CYCLE_START_PIN)) { control_state |= CONTROL_PIN_INDEX_CYCLE_START; }
#endif
#ifdef MACRO_BUTTON_0_PIN
defined_pin_mask |= CONTROL_PIN_INDEX_MACRO_0;
if (digitalRead(MACRO_BUTTON_0_PIN)) { control_state |= CONTROL_PIN_INDEX_MACRO_0; }
#endif
#ifdef MACRO_BUTTON_1_PIN
defined_pin_mask |= CONTROL_PIN_INDEX_MACRO_1;
if (digitalRead(MACRO_BUTTON_1_PIN)) { control_state |= CONTROL_PIN_INDEX_MACRO_1; }
#endif
#ifdef MACRO_BUTTON_2_PIN
defined_pin_mask |= CONTROL_PIN_INDEX_MACRO_2;
if (digitalRead(MACRO_BUTTON_2_PIN)) { control_state |= CONTROL_PIN_INDEX_MACRO_2; }
#endif
#ifdef MACRO_BUTTON_3_PIN
defined_pin_mask |= CONTROL_PIN_INDEX_MACRO_3;
if (digitalRead(MACRO_BUTTON_3_PIN)) { control_state |= CONTROL_PIN_INDEX_MACRO_3; }
#endif
#ifdef INVERT_CONTROL_PIN_MASK
control_state ^= (INVERT_CONTROL_PIN_MASK & defined_pin_mask);
#endif
return(control_state);
}
// Returns limit pin mask according to Grbl internal axis indexing.
uint8_t get_limit_pin_mask(uint8_t axis_idx)
{
if ( axis_idx == X_AXIS ) { return((1<<X_LIMIT_BIT)); }
if ( axis_idx == Y_AXIS ) { return((1<<Y_LIMIT_BIT)); }
if ( axis_idx == Z_AXIS ) { return((1<<Z_LIMIT_BIT)); }
if ( axis_idx == A_AXIS ) { return((1<<A_LIMIT_BIT)); }
if ( axis_idx == B_AXIS ) { return((1<<B_LIMIT_BIT)); }
if ( axis_idx == C_AXIS ) { return((1<<C_LIMIT_BIT)); }
return 0;
}
// execute the function of the control pin
void system_exec_control_pin(uint8_t pin) {
if (bit_istrue(pin,CONTROL_PIN_INDEX_RESET)) {
grbl_send(CLIENT_SERIAL, "[MSG:Reset via control pin]\r\n"); // help debug reason for reset
mc_reset();
}
else if (bit_istrue(pin,CONTROL_PIN_INDEX_CYCLE_START)) {
bit_true(sys_rt_exec_state, EXEC_CYCLE_START);
}
else if (bit_istrue(pin,CONTROL_PIN_INDEX_FEED_HOLD)) {
bit_true(sys_rt_exec_state, EXEC_FEED_HOLD);
}
else if (bit_istrue(pin,CONTROL_PIN_INDEX_SAFETY_DOOR)) {
bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR);
}
#ifdef MACRO_BUTTON_0_PIN
else if (bit_istrue(pin,CONTROL_PIN_INDEX_MACRO_0)) {
user_defined_macro(CONTROL_PIN_INDEX_MACRO_0); // function must be implemented by user
}
#endif
#ifdef MACRO_BUTTON_1_PIN
else if (bit_istrue(pin,CONTROL_PIN_INDEX_MACRO_1)) {
user_defined_macro(CONTROL_PIN_INDEX_MACRO_1); // function must be implemented by user
}
#endif
#ifdef MACRO_BUTTON_2_PIN
else if (bit_istrue(pin,CONTROL_PIN_INDEX_MACRO_2)) {
user_defined_macro(CONTROL_PIN_INDEX_MACRO_2); // function must be implemented by user
}
#endif
#ifdef MACRO_BUTTON_3_PIN
else if (bit_istrue(pin,CONTROL_PIN_INDEX_MACRO_3)) {
user_defined_macro(CONTROL_PIN_INDEX_MACRO_3); // function must be implemented by user
}
#endif
}
// CoreXY calculation only. Returns x or y-axis "steps" based on CoreXY motor steps.
int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps)
{
return( (steps[A_MOTOR] + steps[B_MOTOR])/2 );
}
int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps)
{
return( (steps[A_MOTOR] - steps[B_MOTOR])/2 );
}
// io_num is the virtual pin# and has nothing to do with the actual esp32 GPIO_NUM_xx
// It uses a mask so all can be turned of in ms_reset
void sys_io_control(uint8_t io_num_mask, bool turnOn) {
protocol_buffer_synchronize();
#ifdef USER_DIGITAL_PIN_1
if (io_num_mask & 1<<1) {
digitalWrite(USER_DIGITAL_PIN_1, turnOn);
return;
}
#endif
#ifdef USER_DIGITAL_PIN_2
if (io_num_mask & 1<<2) {
digitalWrite(USER_DIGITAL_PIN_2, turnOn);
return;
}
#endif
#ifdef USER_DIGITAL_PIN_3
if (io_num_mask & 1<<3) {
digitalWrite(USER_DIGITAL_PIN_3, turnOn);
return;
}
#endif
#ifdef USER_DIGITAL_PIN_4
if (io_num_mask & 1<<4) {
digitalWrite(USER_DIGITAL_PIN_4, turnOn);
return;
}
#endif
}

View File

@ -0,0 +1,231 @@
/*
system.h - Header for system level commands and real-time processes
Part of Grbl
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
2018 - Bart Dring This file was modifed for use on the ESP32
CPU. Do not use this with Grbl for atMega328P
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 system_h
#define system_h
#include "grbl.h"
#include "tdef.h"
// Define global system variables
typedef struct {
uint8_t state; // Tracks the current system state of Grbl.
uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
uint8_t suspend; // System suspend bitflag variable that manages holds, cancels, and safety door.
uint8_t soft_limit; // Tracks soft limit errors for the state machine. (boolean)
uint8_t step_control; // Governs the step segment generator depending on system state.
uint8_t probe_succeeded; // Tracks if last probing cycle was successful.
uint8_t homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR.
uint8_t f_override; // Feed rate override value in percent
uint8_t r_override; // Rapids override value in percent
uint8_t spindle_speed_ovr; // Spindle speed value in percent
uint8_t spindle_stop_ovr; // Tracks spindle stop override states
uint8_t report_ovr_counter; // Tracks when to add override data to status reports.
uint8_t report_wco_counter; // Tracks when to add work coordinate offset data to status reports.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
uint8_t override_ctrl; // Tracks override control states.
#endif
float spindle_speed;
} system_t;
extern system_t sys;
// Define system executor bit map. Used internally by realtime protocol as realtime command flags,
// which notifies the main program to execute the specified realtime command asynchronously.
// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default
// flags are always false, so the realtime protocol only needs to check for a non-zero value to
// know when there is a realtime command to execute.
#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001
#define EXEC_CYCLE_START bit(1) // bitmask 00000010
#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100
#define EXEC_FEED_HOLD bit(3) // bitmask 00001000
#define EXEC_RESET bit(4) // bitmask 00010000
#define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000
#define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000
#define EXEC_SLEEP bit(7) // bitmask 10000000
// Alarm executor codes. Valid values (1-255). Zero is reserved.
#define EXEC_ALARM_HARD_LIMIT 1
#define EXEC_ALARM_SOFT_LIMIT 2
#define EXEC_ALARM_ABORT_CYCLE 3
#define EXEC_ALARM_PROBE_FAIL_INITIAL 4
#define EXEC_ALARM_PROBE_FAIL_CONTACT 5
#define EXEC_ALARM_HOMING_FAIL_RESET 6
#define EXEC_ALARM_HOMING_FAIL_DOOR 7
#define EXEC_ALARM_HOMING_FAIL_PULLOFF 8
#define EXEC_ALARM_HOMING_FAIL_APPROACH 9
// Override bit maps. Realtime bitflags to control feed, rapid, spindle, and coolant overrides.
// Spindle/coolant and feed/rapids are separated into two controlling flag variables.
#define EXEC_FEED_OVR_RESET bit(0)
#define EXEC_FEED_OVR_COARSE_PLUS bit(1)
#define EXEC_FEED_OVR_COARSE_MINUS bit(2)
#define EXEC_FEED_OVR_FINE_PLUS bit(3)
#define EXEC_FEED_OVR_FINE_MINUS bit(4)
#define EXEC_RAPID_OVR_RESET bit(5)
#define EXEC_RAPID_OVR_MEDIUM bit(6)
#define EXEC_RAPID_OVR_LOW bit(7)
// #define EXEC_RAPID_OVR_EXTRA_LOW bit(*) // *NOT SUPPORTED*
#define EXEC_SPINDLE_OVR_RESET bit(0)
#define EXEC_SPINDLE_OVR_COARSE_PLUS bit(1)
#define EXEC_SPINDLE_OVR_COARSE_MINUS bit(2)
#define EXEC_SPINDLE_OVR_FINE_PLUS bit(3)
#define EXEC_SPINDLE_OVR_FINE_MINUS bit(4)
#define EXEC_SPINDLE_OVR_STOP bit(5)
#define EXEC_COOLANT_FLOOD_OVR_TOGGLE bit(6)
#define EXEC_COOLANT_MIST_OVR_TOGGLE bit(7)
// Define system state bit map. The state variable primarily tracks the individual functions
// of Grbl to manage each without overlapping. It is also used as a messaging flag for
// critical events.
#define STATE_IDLE 0 // Must be zero. No flags.
#define STATE_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access.
#define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only.
#define STATE_HOMING bit(2) // Performing homing cycle
#define STATE_CYCLE bit(3) // Cycle is running or motions are being executed.
#define STATE_HOLD bit(4) // Active feed hold
#define STATE_JOG bit(5) // Jogging mode.
#define STATE_SAFETY_DOOR bit(6) // Safety door is ajar. Feed holds and de-energizes system.
#define STATE_SLEEP bit(7) // Sleep state.
// Define system suspend flags. Used in various ways to manage suspend states and procedures.
#define SUSPEND_DISABLE 0 // Must be zero.
#define SUSPEND_HOLD_COMPLETE bit(0) // Indicates initial feed hold is complete.
#define SUSPEND_RESTART_RETRACT bit(1) // Flag to indicate a retract from a restore parking motion.
#define SUSPEND_RETRACT_COMPLETE bit(2) // (Safety door only) Indicates retraction and de-energizing is complete.
#define SUSPEND_INITIATE_RESTORE bit(3) // (Safety door only) Flag to initiate resume procedures from a cycle start.
#define SUSPEND_RESTORE_COMPLETE bit(4) // (Safety door only) Indicates ready to resume normal operation.
#define SUSPEND_SAFETY_DOOR_AJAR bit(5) // Tracks safety door state for resuming.
#define SUSPEND_MOTION_CANCEL bit(6) // Indicates a canceled resume motion. Currently used by probing routine.
#define SUSPEND_JOG_CANCEL bit(7) // Indicates a jog cancel in process and to reset buffers when complete.
// Define step segment generator state flags.
#define STEP_CONTROL_NORMAL_OP 0 // Must be zero.
#define STEP_CONTROL_END_MOTION bit(0)
#define STEP_CONTROL_EXECUTE_HOLD bit(1)
#define STEP_CONTROL_EXECUTE_SYS_MOTION bit(2)
#define STEP_CONTROL_UPDATE_SPINDLE_PWM bit(3)
// Define control pin index for Grbl internal use. Pin maps may change, but these values don't.
//#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
#define N_CONTROL_PIN 4
#define CONTROL_PIN_INDEX_SAFETY_DOOR bit(0)
#define CONTROL_PIN_INDEX_RESET bit(1)
#define CONTROL_PIN_INDEX_FEED_HOLD bit(2)
#define CONTROL_PIN_INDEX_CYCLE_START bit(3)
#define CONTROL_PIN_INDEX_MACRO_0 bit(4)
#define CONTROL_PIN_INDEX_MACRO_1 bit(5)
#define CONTROL_PIN_INDEX_MACRO_2 bit(6)
#define CONTROL_PIN_INDEX_MACRO_3 bit(7)
//#else
//#define N_CONTROL_PIN 3
//#define CONTROL_PIN_INDEX_RESET bit(0)
//#define CONTROL_PIN_INDEX_FEED_HOLD bit(1)
//#define CONTROL_PIN_INDEX_CYCLE_START bit(2)
//#endif
// Define spindle stop override control states.
#define SPINDLE_STOP_OVR_DISABLED 0 // Must be zero.
#define SPINDLE_STOP_OVR_ENABLED bit(0)
#define SPINDLE_STOP_OVR_INITIATE bit(1)
#define SPINDLE_STOP_OVR_RESTORE bit(2)
#define SPINDLE_STOP_OVR_RESTORE_CYCLE bit(3)
// NOTE: These position variables may need to be declared as volatiles, if problems arise.
extern int32_t sys_position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
extern int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
extern volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
extern volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
extern volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
extern volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides.
extern volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
#ifdef DEBUG
#define EXEC_DEBUG_REPORT bit(0)
extern volatile uint8_t sys_rt_exec_debug;
#endif
void system_ini(); // Renamed from system_init() due to conflict with esp32 files
// Returns bitfield of control pin states, organized by CONTROL_PIN_INDEX. (1=triggered, 0=not triggered).
uint8_t system_control_get_state();
// Returns if safety door is ajar(T) or closed(F), based on pin state.
uint8_t system_check_safety_door_ajar();
void isr_control_inputs();
// Special handlers for setting and clearing Grbl's real-time execution flags.
void system_set_exec_state_flag(uint8_t mask);
void system_clear_exec_state_flag(uint8_t mask);
void system_set_exec_alarm(uint8_t code);
void system_clear_exec_alarm();
void system_set_exec_motion_override_flag(uint8_t mask);
void system_set_exec_accessory_override_flag(uint8_t mask);
void system_clear_exec_motion_overrides();
void system_clear_exec_accessory_overrides();
// Execute the startup script lines stored in EEPROM upon initialization
void system_execute_startup(char *line);
uint8_t system_execute_line(char *line, uint8_t client);
void system_flag_wco_change();
// Returns machine position of axis 'idx'. Must be sent a 'step' array.
float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx);
// Updates a machine 'position' array based on the 'step' array sent.
void system_convert_array_steps_to_mpos(float *position, int32_t *steps);
// Checks and reports if target array exceeds machine travel limits.
uint8_t system_check_travel_limits(float *target);
uint8_t get_limit_pin_mask(uint8_t axis_idx);
// Special handlers for setting and clearing Grbl's real-time execution flags.
void system_set_exec_state_flag(uint8_t mask);
void system_clear_exec_state_flag(uint8_t mask);
void system_set_exec_alarm(uint8_t code);
void system_clear_exec_alarm();
void system_set_exec_motion_override_flag(uint8_t mask);
void system_set_exec_accessory_override_flag(uint8_t mask);
void system_clear_exec_motion_overrides();
void system_clear_exec_accessory_overrides();
int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps);
int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps);
// A task that runs after a control switch interrupt for debouncing.
void controlCheckTask(void *pvParameters);
void system_exec_control_pin(uint8_t pin);
void sys_io_control(uint8_t io_num_mask, bool turnOn);
#endif

View File

@ -0,0 +1,7 @@
#ifndef tdef_h
#define tdef_h
#include "grbl.h"
#endif

View File

@ -0,0 +1,239 @@
/*
telnet_server.cpp - telnet server functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef ARDUINO_ARCH_ESP32
#include "config.h"
#if defined (ENABLE_WIFI) && defined (ENABLE_TELNET)
#include "wifiservices.h"
#include "grbl.h"
#include "telnet_server.h"
#include "wificonfig.h"
#include <WiFi.h>
#include <Preferences.h>
#include "report.h"
#include "commands.h"
Telnet_Server telnet_server;
bool Telnet_Server::_setupdone = false;
uint16_t Telnet_Server::_port = 0;
WiFiServer * Telnet_Server::_telnetserver = NULL;
WiFiClient Telnet_Server::_telnetClients[MAX_TLNT_CLIENTS];
#ifdef ENABLE_TELNET_WELCOME_MSG
IPAddress Telnet_Server::_telnetClientsIP[MAX_TLNT_CLIENTS];
#endif
Telnet_Server::Telnet_Server(){
_RXbufferSize = 0;
_RXbufferpos = 0;
}
Telnet_Server::~Telnet_Server(){
end();
}
bool Telnet_Server::begin(){
bool no_error = true;
end();
Preferences prefs;
_RXbufferSize = 0;
_RXbufferpos = 0;;
prefs.begin(NAMESPACE, true);
int8_t penabled = prefs.getChar(TELNET_ENABLE_ENTRY, DEFAULT_TELNET_STATE);
//Get telnet port
_port = prefs.getUShort(TELNET_PORT_ENTRY, DEFAULT_TELNETSERVER_PORT);
prefs.end();
if (penabled == 0) return false;
//create instance
_telnetserver= new WiFiServer(_port, MAX_TLNT_CLIENTS);
_telnetserver->setNoDelay(true);
String s = "[MSG:TELNET Started " + String(_port) + "]\r\n";
grbl_send(CLIENT_ALL,(char *)s.c_str());
//start telnet server
_telnetserver->begin();
_setupdone = true;
return no_error;
}
void Telnet_Server::end(){
_setupdone = false;
_RXbufferSize = 0;
_RXbufferpos = 0;
if (_telnetserver) {
delete _telnetserver;
_telnetserver = NULL;
}
}
void Telnet_Server::clearClients(){
//check if there are any new clients
if (_telnetserver->hasClient()){
uint8_t i;
for(i = 0; i < MAX_TLNT_CLIENTS; i++){
//find free/disconnected spot
if (!_telnetClients[i] || !_telnetClients[i].connected()){
#ifdef ENABLE_TELNET_WELCOME_MSG
_telnetClientsIP[i] = IPAddress(0, 0, 0, 0);
#endif
if(_telnetClients[i]) _telnetClients[i].stop();
_telnetClients[i] = _telnetserver->available();
break;
}
}
if (i >= MAX_TLNT_CLIENTS) {
//no free/disconnected spot so reject
_telnetserver->available().stop();
}
}
}
size_t Telnet_Server::write(const uint8_t *buffer, size_t size){
size_t wsize = 0;
if ( !_setupdone || _telnetserver == NULL) {
log_d("[TELNET out blocked]");
return 0;
}
clearClients();
//log_d("[TELNET out]");
//push UART data to all connected telnet clients
for(uint8_t i = 0; i < MAX_TLNT_CLIENTS; i++){
if (_telnetClients[i] && _telnetClients[i].connected()){
//log_d("[TELNET out connected]");
wsize = _telnetClients[i].write(buffer, size);
COMMANDS::wait(0);
}
}
return wsize;
}
void Telnet_Server::handle(){
COMMANDS::wait(0);
//check if can read
if ( !_setupdone || _telnetserver == NULL) {
return;
}
clearClients();
//check clients for data
//uint8_t c;
for(uint8_t i = 0; i < MAX_TLNT_CLIENTS; i++){
if (_telnetClients[i] && _telnetClients[i].connected()){
#ifdef ENABLE_TELNET_WELCOME_MSG
if (_telnetClientsIP[i] != _telnetClients[i].remoteIP()){
report_init_message(CLIENT_TELNET);
_telnetClientsIP[i] = _telnetClients[i].remoteIP();
}
#endif
if(_telnetClients[i].available()){
uint8_t buf[1024];
COMMANDS::wait(0);
int readlen = _telnetClients[i].available();
int writelen = TELNETRXBUFFERSIZE - available();
if (readlen > 1024) readlen = 1024;
if (readlen > writelen) readlen = writelen;
if (readlen > 0) {
_telnetClients[i].read(buf, readlen);
push(buf, readlen);
}
return;
}
}
else {
if (_telnetClients[i]) {
#ifdef ENABLE_TELNET_WELCOME_MSG
_telnetClientsIP[i] = IPAddress(0, 0, 0, 0);
#endif
_telnetClients[i].stop();
}
}
COMMANDS::wait(0);
}
}
int Telnet_Server::peek(void){
if (_RXbufferSize > 0)return _RXbuffer[_RXbufferpos];
else return -1;
}
int Telnet_Server::available(){
return _RXbufferSize;
}
int Telnet_Server::get_rx_buffer_available(){
return TELNETRXBUFFERSIZE - _RXbufferSize;
}
bool Telnet_Server::push (uint8_t data){
log_i("[TELNET]push %c",data);
if ((1 + _RXbufferSize) <= TELNETRXBUFFERSIZE){
int current = _RXbufferpos + _RXbufferSize;
if (current > TELNETRXBUFFERSIZE) current = current - TELNETRXBUFFERSIZE;
if (current > (TELNETRXBUFFERSIZE-1)) current = 0;
_RXbuffer[current] = data;
_RXbufferSize++;
log_i("[TELNET]buffer size %d",_RXbufferSize);
return true;
}
return false;
}
bool Telnet_Server::push (const uint8_t * data, int data_size){
if ((data_size + _RXbufferSize) <= TELNETRXBUFFERSIZE){
int data_processed = 0;
int current = _RXbufferpos + _RXbufferSize;
if (current > TELNETRXBUFFERSIZE) current = current - TELNETRXBUFFERSIZE;
for (int i = 0; i < data_size; i++){
if (current > (TELNETRXBUFFERSIZE-1)) current = 0;
if (char(data[i]) != '\r') {
_RXbuffer[current] = data[i];
current ++;
data_processed++;
}
COMMANDS::wait(0);
//vTaskDelay(1 / portTICK_RATE_MS); // Yield to other tasks
}
_RXbufferSize+=data_processed;
return true;
}
return false;
}
int Telnet_Server::read(void){
if (_RXbufferSize > 0) {
int v = _RXbuffer[_RXbufferpos];
//log_d("[TELNET]read %c",char(v));
_RXbufferpos++;
if (_RXbufferpos > (TELNETRXBUFFERSIZE-1))_RXbufferpos = 0;
_RXbufferSize--;
return v;
} else return -1;
}
#endif // Enable TELNET && ENABLE_WIFI
#endif // ARDUINO_ARCH_ESP32

View File

@ -0,0 +1,68 @@
/*
telnet_server.h - telnet service functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//how many clients should be able to telnet to this ESP32
#define MAX_TLNT_CLIENTS 1
#ifndef _TELNET_SERVER_H
#define _TELNET_SERVER_H
#include "config.h"
class WiFiServer;
class WiFiClient;
#define TELNETRXBUFFERSIZE 1200
#define FLUSHTIMEOUT 500
class Telnet_Server {
public:
Telnet_Server();
~Telnet_Server();
bool begin();
void end();
void handle();
size_t write(const uint8_t *buffer, size_t size);
int read(void);
int peek(void);
int available();
int get_rx_buffer_available();
bool push (uint8_t data);
bool push (const uint8_t * data, int datasize);
static uint16_t port(){return _port;}
private:
static bool _setupdone;
static WiFiServer * _telnetserver;
static WiFiClient _telnetClients[MAX_TLNT_CLIENTS];
#ifdef ENABLE_TELNET_WELCOME_MSG
static IPAddress _telnetClientsIP[MAX_TLNT_CLIENTS];
#endif
static uint16_t _port;
void clearClients();
uint32_t _lastflush;
uint8_t _RXbuffer[TELNETRXBUFFERSIZE];
uint16_t _RXbufferSize;
uint16_t _RXbufferpos;
};
extern Telnet_Server telnet_server;
#endif

View File

@ -0,0 +1,6 @@
G21
G90 (A standard comment)
G1 Z3.810 F228.6 ; a LinuxCNC style comment
G0x0x0 (some lowercase)
G0 X10 (internal comment) Y0
G0X0 (internal comment; with semi colon) Y0Z3

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
/*
web_server.h - wifi services functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _WEB_SERVER_H
#define _WEB_SERVER_H
#include "config.h"
#include "commands.h"
class WebSocketsServer;
class WebServer;
#ifdef ENABLE_AUTHENTICATION
struct auth_ip {
IPAddress ip;
level_authenticate_type level;
char userID[17];
char sessionID[17];
uint32_t last_time;
auth_ip * _next;
};
#endif
class Web_Server {
public:
Web_Server();
~Web_Server();
bool begin();
void end();
void handle();
static long get_client_ID();
static uint16_t port(){return _port;}
private:
static bool _setupdone;
static WebServer * _webserver;
static long _id_connection;
static WebSocketsServer * _socket_server;
static uint16_t _port;
static uint8_t _upload_status;
static String getContentType (String filename);
static String get_Splited_Value(String data, char separator, int index);
static level_authenticate_type is_authenticated();
#ifdef ENABLE_AUTHENTICATION
static auth_ip * _head;
static uint8_t _nb_ip;
static bool AddAuthIP (auth_ip * item);
static char * create_session_ID();
static bool ClearAuthIP (IPAddress ip, const char * sessionID);
static auth_ip * GetAuth (IPAddress ip, const char * sessionID);
static level_authenticate_type ResetAuthIP (IPAddress ip, const char * sessionID);
#endif
#ifdef ENABLE_SSDP
static void handle_SSDP ();
#endif
static void handle_root();
static void handle_login();
static void handle_not_found ();
static void handle_web_command ();
static void handle_web_command_silent ();
static void handle_Websocket_Event(uint8_t num, uint8_t type, uint8_t * payload, size_t length);
static void SPIFFSFileupload ();
static void handleFileList ();
static void handleUpdate ();
static void WebUpdateUpload ();
static bool is_realtime_cmd(char c);
static void pushError(int code, const char * st, bool web_error = 500, uint16_t timeout = 1000);
static void cancelUpload();
#ifdef ENABLE_SD_CARD
static void handle_direct_SDFileList();
static void SDFile_direct_upload();
static bool deleteRecursive(String path);
#endif
};
extern Web_Server web_server;
#endif

View File

@ -0,0 +1,557 @@
/*
wificonfig.cpp - wifi functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef ARDUINO_ARCH_ESP32
#include "config.h"
#ifdef ENABLE_WIFI
#include <WiFi.h>
#include <esp_wifi.h>
#include <ESPmDNS.h>
#include <FS.h>
#include <SPIFFS.h>
#include <Preferences.h>
#include "wificonfig.h"
#include "wifiservices.h"
#include "commands.h"
#include "report.h"
WiFiConfig wifi_config;
String WiFiConfig::_hostname = "";
bool WiFiConfig::_events_registered = false;
WiFiConfig::WiFiConfig(){
}
WiFiConfig::~WiFiConfig(){
end();
}
//just simple helper to convert mac address to string
char * WiFiConfig::mac2str (uint8_t mac [8])
{
static char macstr [18];
if (0 > sprintf (macstr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) ) {
strcpy (macstr, "00:00:00:00:00:00");
}
return macstr;
}
const char *WiFiConfig::info(){
static String result;
String tmp;
result = "[MSG:";
if((WiFi.getMode() == WIFI_MODE_STA ) || (WiFi.getMode() == WIFI_MODE_APSTA )) {
result += "Mode=STA:SSID=";
result += WiFi.SSID();
result += ":Status=";
result += (WiFi.status()==WL_CONNECTED)?"Connected":"Not connected";
result += ":IP=";
result += WiFi.localIP().toString();
result += ":MAC=";
tmp = WiFi.macAddress();
tmp.replace(":","-");
result += tmp;
}
if((WiFi.getMode() == WIFI_MODE_AP ) || (WiFi.getMode() == WIFI_MODE_APSTA )) {
if(WiFi.getMode() == WIFI_MODE_APSTA ) {
result+= "]\r\n[MSG:";
}
result+="Mode=AP:SSDI=";
wifi_config_t conf;
esp_wifi_get_config (ESP_IF_WIFI_AP, &conf);
result+= (const char*)conf.ap.ssid;
result+=":IP=";
result+=WiFi.softAPIP().toString();
result+=":MAC=";
tmp = WiFi.softAPmacAddress();
tmp.replace(":","-");
result += tmp;
}
if(WiFi.getMode() == WIFI_MODE_NULL)result+="No Wifi";
result+= "]\r\n";
return result.c_str();
}
/**
* Helper to convert IP string to int
*/
uint32_t WiFiConfig::IP_int_from_string(String & s){
uint32_t ip_int = 0;
IPAddress ipaddr;
if (ipaddr.fromString(s)) ip_int = ipaddr;
return ip_int;
}
/**
* Helper to convert int to IP string
*/
String WiFiConfig::IP_string_from_int(uint32_t ip_int){
IPAddress ipaddr(ip_int);
return ipaddr.toString();
}
/**
* Check if Hostname string is valid
*/
bool WiFiConfig::isHostnameValid (const char * hostname)
{
//limited size
char c;
if (strlen (hostname) > MAX_HOSTNAME_LENGTH || strlen (hostname) < MIN_HOSTNAME_LENGTH) {
return false;
}
//only letter and digit
for (int i = 0; i < strlen (hostname); i++) {
c = hostname[i];
if (! (isdigit (c) || isalpha (c) || c == '-') ) {
return false;
}
if (c == ' ') {
return false;
}
}
return true;
}
/**
* Check if SSID string is valid
*/
bool WiFiConfig::isSSIDValid (const char * ssid)
{
//limited size
//char c;
if (strlen (ssid) > MAX_SSID_LENGTH || strlen (ssid) < MIN_SSID_LENGTH) {
return false;
}
//only printable
for (int i = 0; i < strlen (ssid); i++) {
if (!isPrintable (ssid[i]) ) {
return false;
}
}
return true;
}
/**
* Check if password string is valid
*/
bool WiFiConfig::isPasswordValid (const char * password)
{
if (strlen (password) == 0) return true; //open network
//limited size
if ((strlen (password) > MAX_PASSWORD_LENGTH) || (strlen (password) < MIN_PASSWORD_LENGTH)) {
return false;
}
//no space allowed ?
/* for (int i = 0; i < strlen (password); i++)
if (password[i] == ' ') {
return false;
}*/
return true;
}
/**
* Check if IP string is valid
*/
bool WiFiConfig::isValidIP(const char * string){
IPAddress ip;
return ip.fromString(string);
}
/**
* WiFi events
* SYSTEM_EVENT_WIFI_READY < ESP32 WiFi ready
* SYSTEM_EVENT_SCAN_DONE < ESP32 finish scanning AP
* SYSTEM_EVENT_STA_START < ESP32 station start
* SYSTEM_EVENT_STA_STOP < ESP32 station stop
* SYSTEM_EVENT_STA_CONNECTED < ESP32 station connected to AP
* SYSTEM_EVENT_STA_DISCONNECTED < ESP32 station disconnected from AP
* SYSTEM_EVENT_STA_AUTHMODE_CHANGE < the auth mode of AP connected by ESP32 station changed
* SYSTEM_EVENT_STA_GOT_IP < ESP32 station got IP from connected AP
* SYSTEM_EVENT_STA_LOST_IP < ESP32 station lost IP and the IP is reset to 0
* SYSTEM_EVENT_STA_WPS_ER_SUCCESS < ESP32 station wps succeeds in enrollee mode
* SYSTEM_EVENT_STA_WPS_ER_FAILED < ESP32 station wps fails in enrollee mode
* SYSTEM_EVENT_STA_WPS_ER_TIMEOUT < ESP32 station wps timeout in enrollee mode
* SYSTEM_EVENT_STA_WPS_ER_PIN < ESP32 station wps pin code in enrollee mode
* SYSTEM_EVENT_AP_START < ESP32 soft-AP start
* SYSTEM_EVENT_AP_STOP < ESP32 soft-AP stop
* SYSTEM_EVENT_AP_STACONNECTED < a station connected to ESP32 soft-AP
* SYSTEM_EVENT_AP_STADISCONNECTED < a station disconnected from ESP32 soft-AP
* SYSTEM_EVENT_AP_PROBEREQRECVED < Receive probe request packet in soft-AP interface
* SYSTEM_EVENT_GOT_IP6 < ESP32 station or ap or ethernet interface v6IP addr is preferred
* SYSTEM_EVENT_ETH_START < ESP32 ethernet start
* SYSTEM_EVENT_ETH_STOP < ESP32 ethernet stop
* SYSTEM_EVENT_ETH_CONNECTED < ESP32 ethernet phy link up
* SYSTEM_EVENT_ETH_DISCONNECTED < ESP32 ethernet phy link down
* SYSTEM_EVENT_ETH_GOT_IP < ESP32 ethernet got IP from connected AP
* SYSTEM_EVENT_MAX
*/
void WiFiConfig::WiFiEvent(WiFiEvent_t event)
{
switch (event)
{
case SYSTEM_EVENT_STA_GOT_IP:
grbl_sendf(CLIENT_ALL,"[MSG:Connected with %s]\r\n",WiFi.localIP().toString().c_str());
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
grbl_send(CLIENT_ALL,"[MSG:Disconnected]\r\n");
break;
default:
break;
}
}
/*
* Get WiFi signal strength
*/
int32_t WiFiConfig::getSignal (int32_t RSSI)
{
if (RSSI <= -100) {
return 0;
}
if (RSSI >= -50) {
return 100;
}
return (2 * (RSSI + 100) );
}
/*
* Connect client to AP
*/
bool WiFiConfig::ConnectSTA2AP(){
String msg, msg_out;
uint8_t count = 0;
uint8_t dot = 0;
wl_status_t status = WiFi.status();
while (status != WL_CONNECTED && count < 40) {
switch (status) {
case WL_NO_SSID_AVAIL:
msg="No SSID";
break;
case WL_CONNECT_FAILED:
msg="Connection failed";
break;
case WL_CONNECTED:
break;
default:
if ((dot>3) || (dot==0) ){
dot=0;
msg_out = "Connecting";
}
msg_out+=".";
msg= msg_out;
dot++;
break;
}
grbl_sendf(CLIENT_ALL,"[MSG:%s]\r\n",msg.c_str());
COMMANDS::wait (500);
count++;
status = WiFi.status();
}
return (status == WL_CONNECTED);
}
/*
* Start client mode (Station)
*/
bool WiFiConfig::StartSTA(){
String defV;
Preferences prefs;
//stop active service
wifi_services.end();
//Sanity check
if((WiFi.getMode() == WIFI_STA) || (WiFi.getMode() == WIFI_AP_STA))WiFi.disconnect();
if((WiFi.getMode() == WIFI_AP) || (WiFi.getMode() == WIFI_AP_STA))WiFi.softAPdisconnect();
WiFi.enableAP (false);
WiFi.mode(WIFI_STA);
//Get parameters for STA
prefs.begin(NAMESPACE, true);
defV = DEFAULT_HOSTNAME;
String h = prefs.getString(HOSTNAME_ENTRY, defV);
WiFi.setHostname(h.c_str());
//SSID
defV = DEFAULT_STA_SSID;
String SSID = prefs.getString(STA_SSID_ENTRY, defV);
if (SSID.length() == 0)SSID = DEFAULT_STA_SSID;
//password
defV = DEFAULT_STA_PWD;
String password = prefs.getString(STA_PWD_ENTRY, defV);
int8_t IP_mode = prefs.getChar(STA_IP_MODE_ENTRY, DHCP_MODE);
//IP
defV = DEFAULT_STA_IP;
int32_t IP = prefs.getInt(STA_IP_ENTRY, IP_int_from_string(defV));
//GW
defV = DEFAULT_STA_GW;
int32_t GW = prefs.getInt(STA_GW_ENTRY, IP_int_from_string(defV));
//MK
defV = DEFAULT_STA_MK;
int32_t MK = prefs.getInt(STA_MK_ENTRY, IP_int_from_string(defV));
prefs.end();
//if not DHCP
if (IP_mode != DHCP_MODE) {
IPAddress ip(IP), mask(MK), gateway(GW);
WiFi.config(ip, gateway,mask);
}
if (WiFi.begin(SSID.c_str(), (password.length() > 0)?password.c_str():NULL)){
grbl_send(CLIENT_ALL,"\n[MSG:Client Started]\r\n");
grbl_sendf(CLIENT_ALL,"[MSG:Connecting %s]\r\n", SSID.c_str());
return ConnectSTA2AP();
} else {
grbl_send(CLIENT_ALL,"[MSG:Starting client failed]\r\n");
return false;
}
}
/**
* Setup and start Access point
*/
bool WiFiConfig::StartAP(){
String defV;
Preferences prefs;
//stop active services
wifi_services.end();
//Sanity check
if((WiFi.getMode() == WIFI_STA) || (WiFi.getMode() == WIFI_AP_STA))WiFi.disconnect();
if((WiFi.getMode() == WIFI_AP) || (WiFi.getMode() == WIFI_AP_STA))WiFi.softAPdisconnect();
WiFi.enableSTA (false);
WiFi.mode(WIFI_AP);
//Get parameters for AP
prefs.begin(NAMESPACE, true);
//SSID
defV = DEFAULT_AP_SSID;
String SSID = prefs.getString(AP_SSID_ENTRY, defV);
if (SSID.length() == 0)SSID = DEFAULT_AP_SSID;
//password
defV = DEFAULT_AP_PWD;
String password = prefs.getString(AP_PWD_ENTRY, defV);
//channel
int8_t channel = prefs.getChar(AP_CHANNEL_ENTRY, DEFAULT_AP_CHANNEL);
if (channel == 0)channel = DEFAULT_AP_CHANNEL;
//IP
defV = DEFAULT_AP_IP;
int32_t IP = prefs.getInt(AP_IP_ENTRY, IP_int_from_string(defV));
if (IP==0){
IP = IP_int_from_string(defV);
}
prefs.end();
IPAddress ip(IP);
IPAddress mask;
mask.fromString(DEFAULT_AP_MK);
//Set static IP
WiFi.softAPConfig(ip, ip, mask);
//Start AP
if(WiFi.softAP(SSID.c_str(), (password.length() > 0)?password.c_str():NULL, channel)) {
grbl_sendf(CLIENT_ALL,"\n[MSG:Local access point %s started, %s]\r\n", SSID.c_str(), WiFi.softAPIP().toString().c_str());
return true;
} else {
grbl_send(CLIENT_ALL,"[MSG:Starting AP failed]\r\n");
return false;
}
}
/**
* Stop WiFi
*/
void WiFiConfig::StopWiFi(){
//Sanity check
if((WiFi.getMode() == WIFI_STA) || (WiFi.getMode() == WIFI_AP_STA))WiFi.disconnect(true);
if((WiFi.getMode() == WIFI_AP) || (WiFi.getMode() == WIFI_AP_STA))WiFi.softAPdisconnect(true);
wifi_services.end();
WiFi.enableSTA (false);
WiFi.enableAP (false);
WiFi.mode(WIFI_OFF);
grbl_send(CLIENT_ALL,"\n[MSG:WiFi Off]\r\n");
}
/**
* begin WiFi setup
*/
void WiFiConfig::begin() {
Preferences prefs;
//stop active services
wifi_services.end();
//setup events
if (!_events_registered) {
//cumulative function and no remove so only do once
WiFi.onEvent(WiFiConfig::WiFiEvent);
_events_registered = true;
}
//open preferences as read-only
prefs.begin(NAMESPACE, true);
//Get hostname
String defV = DEFAULT_HOSTNAME;
_hostname = prefs.getString(HOSTNAME_ENTRY, defV);
int8_t wifiMode = prefs.getChar(ESP_RADIO_MODE, DEFAULT_RADIO_MODE);
if (wifiMode == ESP_WIFI_AP) {
StartAP();
//start services
wifi_services.begin();
} else if (wifiMode == ESP_WIFI_STA){
if(!StartSTA()){
defV = DEFAULT_STA_SSID;
grbl_sendf(CLIENT_ALL,"[MSG:Cannot connect to %s]\r\n", prefs.getString(STA_SSID_ENTRY, defV).c_str());
StartAP();
}
//start services
wifi_services.begin();
}else WiFi.mode(WIFI_OFF);
prefs.end();
}
/**
* End WiFi
*/
void WiFiConfig::end() {
StopWiFi();
}
/**
* Reset ESP
*/
void WiFiConfig::reset_settings(){
Preferences prefs;
prefs.begin(NAMESPACE, false);
String sval;
int8_t bbuf;
int16_t ibuf;
bool error = false;
sval = DEFAULT_HOSTNAME;
if (prefs.putString(HOSTNAME_ENTRY, sval) == 0){
error = true;
}
bbuf = DEFAULT_NOTIFICATION_TYPE;
if (prefs.putChar(NOTIFICATION_TYPE, bbuf) ==0 ) {
error = true;
}
sval = DEFAULT_TOKEN;
if (prefs.putString(NOTIFICATION_T1, sval) != sval.length()){
error = true;
}
if (prefs.putString(NOTIFICATION_T2, sval) != sval.length()){
error = true;
}
if (prefs.putString(NOTIFICATION_TS, sval) != sval.length()){
error = true;
}
sval = DEFAULT_STA_SSID;
if (prefs.putString(STA_SSID_ENTRY, sval) == 0){
error = true;
}
sval = DEFAULT_STA_PWD;
if (prefs.putString(STA_PWD_ENTRY, sval) != sval.length()){
error = true;
}
sval = DEFAULT_AP_SSID;
if (prefs.putString(AP_SSID_ENTRY, sval) == 0){
error = true;
}
sval = DEFAULT_AP_PWD;
if (prefs.putString(AP_PWD_ENTRY, sval) != sval.length()){
error = true;
}
bbuf = DEFAULT_AP_CHANNEL;
if (prefs.putChar(AP_CHANNEL_ENTRY, bbuf) ==0 ) {
error = true;
}
bbuf = DEFAULT_STA_IP_MODE;
if (prefs.putChar(STA_IP_MODE_ENTRY, bbuf) ==0 ) {
error = true;
}
bbuf = DEFAULT_HTTP_STATE;
if (prefs.putChar(HTTP_ENABLE_ENTRY, bbuf) ==0 ) {
error = true;
}
bbuf = DEFAULT_TELNET_STATE;
if (prefs.putChar(TELNET_ENABLE_ENTRY, bbuf) ==0 ) {
error = true;
}
bbuf = DEFAULT_RADIO_MODE;
if (prefs.putChar(ESP_RADIO_MODE, bbuf) ==0 ) {
error = true;
}
ibuf = DEFAULT_WEBSERVER_PORT;
if (prefs.putUShort(HTTP_PORT_ENTRY, ibuf) == 0) {
error = true;
}
ibuf = DEFAULT_TELNETSERVER_PORT;
if (prefs.putUShort(TELNET_PORT_ENTRY, ibuf) == 0) {
error = true;
}
sval = DEFAULT_STA_IP;
if (prefs.putInt(STA_IP_ENTRY, wifi_config.IP_int_from_string(sval)) == 0) {
error = true;
}
sval = DEFAULT_STA_GW;
if (prefs.putInt(STA_GW_ENTRY, wifi_config.IP_int_from_string(sval)) == 0) {
error = true;
}
sval = DEFAULT_STA_MK;
if (prefs.putInt(STA_MK_ENTRY, wifi_config.IP_int_from_string(sval)) == 0) {
error = true;
}
sval = DEFAULT_AP_IP;
if (prefs.putInt(AP_IP_ENTRY, wifi_config.IP_int_from_string(sval)) == 0) {
error = true;
}
prefs.end();
if (error) {
grbl_send(CLIENT_ALL,"[MSG:WiFi reset error]\r\n");
} else {
grbl_send(CLIENT_ALL,"[MSG:WiFi reset done]\r\n");
}
}
bool WiFiConfig::Is_WiFi_on(){
return !(WiFi.getMode() == WIFI_MODE_NULL);
}
/**
* Handle not critical actions that must be done in sync environement
*/
void WiFiConfig::handle() {
//Services
COMMANDS::wait(0);
wifi_services.handle();
}
#endif // ENABLE_WIFI
#endif // ARDUINO_ARCH_ESP32

View File

@ -0,0 +1,129 @@
/*
wificonfig.h - wifi functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
//Preferences entries
#define HOSTNAME_ENTRY "ESP_HOSTNAME"
#define STA_SSID_ENTRY "STA_SSID"
#define STA_PWD_ENTRY "STA_PWD"
#define STA_IP_ENTRY "STA_IP"
#define STA_GW_ENTRY "STA_GW"
#define STA_MK_ENTRY "STA_MK"
#define AP_SSID_ENTRY "AP_SSID"
#define AP_PWD_ENTRY "AP_PWD"
#define AP_IP_ENTRY "AP_IP"
#define AP_CHANNEL_ENTRY "AP_CHANNEL"
#define HTTP_ENABLE_ENTRY "HTTP_ON"
#define HTTP_PORT_ENTRY "HTTP_PORT"
#define TELNET_ENABLE_ENTRY "TELNET_ON"
#define TELNET_PORT_ENTRY "TELNET_PORT"
#define STA_IP_MODE_ENTRY "STA_IP_MODE"
#define NOTIFICATION_TYPE "NOTIF_TYPE"
#define NOTIFICATION_T1 "NOTIF_T1"
#define NOTIFICATION_T2 "NOTIF_T2"
#define NOTIFICATION_TS "NOTIF_TS"
//Notifications
#define ESP_PUSHOVER_NOTIFICATION 1
#define ESP_EMAIL_NOTIFICATION 2
#define ESP_LINE_NOTIFICATION 3
#define DHCP_MODE 0
#define STATIC_MODE 1
//Switch
#define ESP_SAVE_ONLY 0
#define ESP_APPLY_NOW 1
//defaults values
#define DEFAULT_HOSTNAME "grblesp"
#define DEFAULT_STA_SSID "GRBL_ESP"
#define DEFAULT_STA_PWD "12345678"
#define DEFAULT_STA_IP "0.0.0.0"
#define DEFAULT_STA_GW "0.0.0.0"
#define DEFAULT_STA_MK "0.0.0.0"
#define DEFAULT_AP_SSID "GRBL_ESP"
#define DEFAULT_AP_PWD "12345678"
#define DEFAULT_AP_IP "192.168.0.1"
#define DEFAULT_AP_MK "255.255.255.0"
#define DEFAULT_AP_CHANNEL 1
#define DEFAULT_WEBSERVER_PORT 80
#define DEFAULT_HTTP_STATE 1
#define DEFAULT_TELNETSERVER_PORT 23
#define DEFAULT_TELNET_STATE 1
#define DEFAULT_STA_IP_MODE DHCP_MODE
#define HIDDEN_PASSWORD "********"
#define DEFAULT_TOKEN ""
#define DEFAULT_NOTIFICATION_TYPE 0
//boundaries
#define MAX_SSID_LENGTH 32
#define MIN_SSID_LENGTH 1
#define MAX_PASSWORD_LENGTH 64
//min size of password is 0 or upper than 8 char
//so let set min is 8
#define MIN_PASSWORD_LENGTH 8
#define MAX_HOSTNAME_LENGTH 32
#define MIN_HOSTNAME_LENGTH 1
#define MAX_HTTP_PORT 65001
#define MIN_HTTP_PORT 1
#define MAX_TELNET_PORT 65001
#define MIN_TELNET_PORT 1
#define MIN_CHANNEL 1
#define MAX_CHANNEL 14
#define MIN_NOTIFICATION_TOKEN_LENGTH 0
#define MAX_NOTIFICATION_TOKEN_LENGTH 63
#define MAX_NOTIFICATION_SETTING_LENGTH 127
#ifndef _WIFI_CONFIG_H
#define _WIFI_CONFIG_H
#include "WiFi.h"
class WiFiConfig {
public:
WiFiConfig();
~WiFiConfig();
static const char *info();
static bool isValidIP(const char * string);
static bool isPasswordValid (const char * password);
static bool isSSIDValid (const char * ssid);
static bool isHostnameValid (const char * hostname);
static uint32_t IP_int_from_string(String & s);
static String IP_string_from_int(uint32_t ip_int);
static String Hostname(){return _hostname;}
static char * mac2str (uint8_t mac [8]);
static bool StartAP();
static bool StartSTA();
static void StopWiFi();
static int32_t getSignal (int32_t RSSI);
static void begin();
static void end();
static void handle();
static void reset_settings();
static bool Is_WiFi_on();
private :
static bool ConnectSTA2AP();
static void WiFiEvent(WiFiEvent_t event);
static String _hostname;
static bool _events_registered;
};
extern WiFiConfig wifi_config;
#endif

View File

@ -0,0 +1,173 @@
/*
wifiservices.cpp - wifi services functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef ARDUINO_ARCH_ESP32
#include "config.h"
#ifdef ENABLE_WIFI
#include <WiFi.h>
#include <FS.h>
#include <SPIFFS.h>
#include <Preferences.h>
#include "report.h"
#include "wificonfig.h"
#include "wifiservices.h"
#ifdef ENABLE_MDNS
#include <ESPmDNS.h>
#endif
#ifdef ENABLE_OTA
#include <ArduinoOTA.h>
#endif
#ifdef ENABLE_HTTP
#include "web_server.h"
#endif
#ifdef ENABLE_TELNET
#include "telnet_server.h"
#endif
#ifdef ENABLE_NOTIFICATIONS
#include "notifications_service.h"
#endif
#include "commands.h"
WiFiServices wifi_services;
WiFiServices::WiFiServices(){
}
WiFiServices::~WiFiServices(){
end();
}
bool WiFiServices::begin(){
bool no_error = true;
//Sanity check
if(WiFi.getMode() == WIFI_OFF) return false;
String h;
Preferences prefs;
//Get hostname
String defV = DEFAULT_HOSTNAME;
prefs.begin(NAMESPACE, true);
h = prefs.getString(HOSTNAME_ENTRY, defV);
prefs.end();
//Start SPIFFS
SPIFFS.begin(true);
#ifdef ENABLE_OTA
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else {// U_SPIFFS
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
type = "filesystem";
SPIFFS.end();
}
grbl_sendf(CLIENT_ALL,"[MSG:Start OTA updating %s]\r\n", type.c_str());
})
.onEnd([]() {
grbl_sendf(CLIENT_ALL,"[MSG:End OTA]\r\n");
})
.onProgress([](unsigned int progress, unsigned int total) {
grbl_sendf(CLIENT_ALL,"[MSG:OTA Progress: %u%%]\r\n", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
grbl_sendf(CLIENT_ALL,"[MSG:OTA Error(%u):]\r\n", error);
if (error == OTA_AUTH_ERROR) grbl_send(CLIENT_ALL,"[MSG:Auth Failed]\r\n");
else if (error == OTA_BEGIN_ERROR) grbl_send(CLIENT_ALL,"[MSG:Begin Failed]\r\n");
else if (error == OTA_CONNECT_ERROR) grbl_send(CLIENT_ALL,"[MSG:Connect Failed]\r\n");
else if (error == OTA_RECEIVE_ERROR) grbl_send(CLIENT_ALL,"[MSG:Receive Failed]\r\n");
else if (error == OTA_END_ERROR) grbl_send(CLIENT_ALL,"[MSG:End Failed]\r\n");
});
ArduinoOTA.begin();
#endif
#ifdef ENABLE_MDNS
//no need in AP mode
if(WiFi.getMode() == WIFI_STA){
//start mDns
if (!MDNS.begin(h.c_str())) {
grbl_send(CLIENT_ALL,"[MSG:Cannot start mDNS]\r\n");
no_error = false;
} else {
grbl_sendf(CLIENT_ALL,"[MSG:Start mDNS with hostname:http://%s.local/]\r\n",h.c_str());
}
}
#endif
#ifdef ENABLE_HTTP
web_server.begin();
#endif
#ifdef ENABLE_TELNET
telnet_server.begin();
#endif
#ifdef ENABLE_NOTIFICATIONS
notificationsservice.begin();
#endif
//be sure we are not is mixed mode in setup
WiFi.scanNetworks (true);
return no_error;
}
void WiFiServices::end(){
#ifdef ENABLE_NOTIFICATIONS
notificationsservice.end();
#endif
#ifdef ENABLE_TELNET
telnet_server.end();
#endif
#ifdef ENABLE_HTTP
web_server.end();
#endif
//stop OTA
#ifdef ENABLE_OTA
ArduinoOTA.end();
#endif
//Stop SPIFFS
SPIFFS.end();
#ifdef ENABLE_MDNS
//Stop mDNS
MDNS.end();
#endif
}
void WiFiServices::handle(){
COMMANDS::wait(0);
//to avoid mixed mode due to scan network
if (WiFi.getMode() == WIFI_AP_STA) {
if (WiFi.scanComplete() != WIFI_SCAN_RUNNING) {
WiFi.enableSTA (false);
}
}
#ifdef ENABLE_OTA
ArduinoOTA.handle();
#endif
#ifdef ENABLE_HTTP
web_server.handle();
#endif
#ifdef ENABLE_TELNET
telnet_server.handle();
#endif
}
#endif // ENABLE_WIFI
#endif // ARDUINO_ARCH_ESP32

View File

@ -0,0 +1,39 @@
/*
wifiservices.h - wifi services functions class
Copyright (c) 2014 Luc Lebosse. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _WIFI_SERVICES_H
#define _WIFI_SERVICES_H
class WiFiServices {
public:
WiFiServices();
~WiFiServices();
static bool begin();
static void end();
static void handle();
};
extern WiFiServices wifi_services;
#endif

674
Grbl_Esp32-master/LICENSE Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@ -0,0 +1,56 @@
# Grbl (CNC Controller) For ESP32
![ESP32](http://www.buildlog.net/blog/wp-content/uploads/2018/10/20181007_153826.jpg)
### Project Overview
[![Build Status](https://travis-ci.com/odaki/Grbl_Esp32.svg?branch=master)](https://travis-ci.com/odaki/Grbl_Esp32)
This is a port of [Grbl](https://github.com/gnea/grbl) for the ESP32. The ESP32 is potentially a great target for Grbl for the following reasons
- **Faster** - At least 4x the step rates over Grbl
- **Lower Cost** -
- **Small footprint** -
- **More Flash and RAM** - A larger planner buffer could be used and more features could be added.
- **I/O** - It has just about the same number of pins as an Arduino UNO, the original target for Grbl
- **Peripherals** - It has more timers and advanced features than an UNO. These can also be mapped to pins more flexibly.
- **Connectivity** - Bluetooth and WiFi built in.
- **Fast Boot** - Boots almost instantly and does not need to be formally shutdown (unlike Raspberry Pi or Beagle Bone)
- **RTOS (Real Time operating System)** - Custom features can be added without affecting the performance of the motion control system.
### Using It
The code should be compiled using the latest Arduino IDE. [Follow instructions here](https://github.com/espressif/arduino-esp32) on how to setup ESP32 in the IDE. The choice was made to use the Arduino IDE over the ESP-IDF to make the code a little more accessible to novices trying to compile the code.
I use the ESP32 Dev Module version of the ESP32. I suggest starting with that if you don't have hardware yet.
For basic instructions on using Grbl use the [gnea/grbl wiki](https://github.com/gnea/grbl/wiki). That is the Arduino version of Grbl, so keep that in mind regarding hardware setup. If you have questions ask via the GitHub issue system for this project.
### Roadmap
The roadmap is now [on the wiki](https://github.com/bdring/Grbl_Esp32/wiki/Development-Roadmap).
### Credits
The original [Grbl](https://github.com/gnea/grbl) is an awesome project by Sungeon (Sonny) Jeon. I have known him for many years and he is always very helpful. I have used Grbl on many projects. I only ported because of the limitation of the processors it was designed for. The core engine design is virtually unchanged.
The Wifi and WebUI is based on [this project.](https://github.com/luc-github/ESP3D-WEBUI)
### Contribute
![](http://www.buildlog.net/blog/wp-content/uploads/2018/07/slack_hash_128.png) There is a slack channel for the development this project. Ask for an Invite
### FAQ
Start asking questions...I'll put the frequent ones here.
### <a name="donation"></a>Donation
This project requires a lot of work and often expensive items for testing. Please consider a safe, secure and highly appreciated donation via the PayPal link below.
[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TKNJ9Z775VXB2)

View File

@ -0,0 +1,19 @@
#!/bin/bash
function build_sketch()
{
local sketch=$1
# buld sketch with arudino ide
echo -e "\n Build $sketch \n"
arduino --verbose --verify $sketch
# get build result from arduino
local re=$?
# check result
if [ $re -ne 0 ]; then
echo "Failed to build $sketch"
return $re
fi
}

View File

@ -0,0 +1,153 @@
This file defines the [ESP...] style commands. They can be used via serial, Bluetooth or Wifi
These are used for basic wireless settings as well as SD card commands
Some commands need to be authenticated with a password. Authentication
is optional via #define ENABLE_AUTHENTICATION in the config.h file.
The default password is "admin"
============== Examples (assumes "admin" password): ===============
Command: [ESP800]pwd=admin
Reply: FW version:1.1f # FW target:grbl-embedded # FW HW:Direct SD # primary sd:/sd # secondary sd:none # authentication:yes # webcommunication: Sync: 81# hostname:grblesp
Command: [ESP111]
Reply: 192.168.1.11
Command: [ESP121]pwd=admin
Reply: 80
Command: [ESP121]pwd=admin
Reply: 80
Command: [ESP121]81 pwd=admin
Reply: ok
Command: [ESP121]pwd=admin
Reply: 81
=============== Command Reference ========================
* Set/Get STA SSID
[ESP100]<SSID>pwd=<admin password>
* Set STA Password
[ESP101]<Password>pwd=<admin password>
* Set/Get STA IP mode (DHCP/STATIC)
[ESP102]<mode>pwd=<admin password>
* Set/Get STA IP/Mask/GW
[ESP103]IP=<IP> MSK=<IP> GW=<IP> pwd=<admin password>
* Set/Get AP SSID
[ESP105]<SSID>pwd=<admin password>
* Change AP Password
[ESP106]<Password>pwd=<admin password>
* Set/Get AP IP
[ESP107]<IP>pwd=<admin password>
* Set/Get AP channel
[ESP108]<channel>pwd=<admin password>
* Set/Get radio state which can be STA, AP, BT, OFF
[ESP110]<state>pwd=<admin password>
* Get current IP
[ESP111]<header answer>
* Get/Set hostname
[ESP112]<Hostname> pwd=<admin password>
* Get/Set immediate Radio (WiFi/BT) state which can be ON, OFF
[ESP115]<state>pwd=<admin password>
* Get/Set HTTP state which can be ON, OFF
[ESP120]<state>pwd=<admin password>
* Get/Set HTTP port
[ESP121]<port>pwd=<admin password>
* Get/Set Telnet state which can be ON, OFF
[ESP130]<state>pwd=<admin password>
* Get/Set Telnet port
[ESP131]<port>pwd=<admin password>
* Get/Set btname
[ESP140]< Bluetooth name> pwd=<admin password>
* Get SD Card Status
[ESP200] pwd=<user/admin password>
* Get SD Card Content
[ESP210] pwd=<user/admin password>
* Delete SD Card file / directory
[ESP215]<file/dir name>pwd=<user/admin password>
* Print SD file
[ESP220] <Filename> pwd=<user/admin password>
*Get full EEPROM settings content
but do not give any passwords
[ESP400] pwd=<user/admin password>
*Set EEPROM setting
position in EEPROM, type: B(byte), I(integer/long), S(string), A(IP address / mask)
[ESP401]P=<position> T=<type> V=<value> pwd=<user/admin password>
Positions:
HOSTNAME_ENTRY "ESP_HOSTNAME" String
STA_SSID_ENTRY "STA_SSID" String
STA_PWD_ENTRY "STA_PWD" String
STA_IP_ENTRY "STA_IP" IP
STA_GW_ENTRY "STA_GW" IP
STA_MK_ENTRY "STA_MK" IP
ESP_WIFI_MODE "WIFI_MODE" Byte (0=OFF, STA=1, AP=2)
AP_SSID_ENTRY "AP_SSID" String
AP_PWD_ENTRY "AP_PWD" String
AP_IP_ENTRY "AP_IP" IP
AP_CHANNEL_ENTRY "AP_CHANNEL" Byte
HTTP_ENABLE_ENTRY "HTTP_ON" Byte (0=Disabled, 1=Enabled)
HTTP_PORT_ENTRY "HTTP_PORT" Integer
TELNET_ENABLE_ENTRY "TELNET_ON" Byte (0=Disabled, 1=Enabled)
TELNET_PORT_ENTRY "TELNET_PORT" Integer
STA_IP_MODE_ENTRY "STA_IP_MODE" Byte (0=DHCP, 1=STATIC)
*Get available AP list (limited to 30)
output is JSON or plain text according parameter
[ESP410] pwd=<user/admin password>
*Get current settings of ESP3D
output is JSON or plain text according parameter
[ESP420]pwd=<user/admin password>
* Restart ESP
[ESP444]RESTART pwd=<admin password>
* Change / Reset user password
[ESP555]<password>pwd=<admin password>
if no password set it use default one
* Send Notification
[ESP600]msg [pwd=<admin password>]
* Set/Get Notification settings
[ESP610]type=<NONE/PUSHOVER/EMAIL/LINE> T1=<token1> T2=<token2> TS=<Settings> [pwd=<admin password>]
Get will give type and settings only, not the protected T1/T2
* Read SPIFFS file and send each line to serial
[ESP700]<filename> pwd=<user/admin password>
* Format SPIFFS
[ESP710]FORMAT pwd=<admin password>
* SPIFFS total size and used size
[ESP720]<header answer> pwd=<user/admin password>
* Get fw version and basic information
[ESP800]<header answer>

View File

@ -0,0 +1,10 @@
"Alarm Code in v1.1+"," Alarm Message in v1.0-"," Alarm Description"
"1","Hard limit","Hard limit has been triggered. Machine position is likely lost due to sudden halt. Re-homing is highly recommended."
"2","Soft limit","Soft limit alarm. G-code motion target exceeds machine travel. Machine position retained. Alarm may be safely unlocked."
"3","Abort during cycle","Reset while in motion. Machine position is likely lost due to sudden halt. Re-homing is highly recommended."
"4","Probe fail","Probe fail. Probe is not in the expected initial state before starting probe cycle when G38.2 and G38.3 is not triggered and G38.4 and G38.5 is triggered."
"5","Probe fail","Probe fail. Probe did not contact the workpiece within the programmed travel for G38.2 and G38.4."
"6","Homing fail","Homing fail. The active homing cycle was reset."
"7","Homing fail","Homing fail. Safety door was opened during homing cycle."
"8","Homing fail","Homing fail. Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring."
"9","Homing fail","Homing fail. Could not find limit switch within search distances. Try increasing max travel, decreasing pull-off distance, or check wiring."
1 Alarm Code in v1.1+ Alarm Message in v1.0- Alarm Description
2 1 Hard limit Hard limit has been triggered. Machine position is likely lost due to sudden halt. Re-homing is highly recommended.
3 2 Soft limit Soft limit alarm. G-code motion target exceeds machine travel. Machine position retained. Alarm may be safely unlocked.
4 3 Abort during cycle Reset while in motion. Machine position is likely lost due to sudden halt. Re-homing is highly recommended.
5 4 Probe fail Probe fail. Probe is not in the expected initial state before starting probe cycle when G38.2 and G38.3 is not triggered and G38.4 and G38.5 is triggered.
6 5 Probe fail Probe fail. Probe did not contact the workpiece within the programmed travel for G38.2 and G38.4.
7 6 Homing fail Homing fail. The active homing cycle was reset.
8 7 Homing fail Homing fail. Safety door was opened during homing cycle.
9 8 Homing fail Homing fail. Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring.
10 9 Homing fail Homing fail. Could not find limit switch within search distances. Try increasing max travel, decreasing pull-off distance, or check wiring.

View File

@ -0,0 +1,22 @@
OPT: Code, Build-Option Description,State
V,Variable spindle,Enabled
N,Line numbers,Enabled
M,Mist coolant M7,Enabled
C,CoreXY,Enabled
P,Parking motion,Enabled
Z,Homing force origin,Enabled
H,Homing single axis commands,Enabled
T,Two limit switches on axis,Enabled
A,Allow feed rate overrides in probe cycles,Enabled
D,Use spindle direction as enable pin,Enabled
0,Spindle enable off when speed is zero,Enabled
S,Software limit pin debouncing,Enabled
R,Parking override control,Enabled
+,Safety door input pin,Enabled
*,Restore all EEPROM command,Disabled
$,Restore EEPROM `$` settings command,Disabled
#,Restore EEPROM parameter data command,Disabled
I,Build info write user string command,Disabled
E,Force sync upon EEPROM write,Disabled
W,Force sync upon work coordinate offset change,Disabled
L,Homing initialization auto-lock,Disabled
1 OPT: Code Build-Option Description State
2 V Variable spindle Enabled
3 N Line numbers Enabled
4 M Mist coolant M7 Enabled
5 C CoreXY Enabled
6 P Parking motion Enabled
7 Z Homing force origin Enabled
8 H Homing single axis commands Enabled
9 T Two limit switches on axis Enabled
10 A Allow feed rate overrides in probe cycles Enabled
11 D Use spindle direction as enable pin Enabled
12 0 Spindle enable off when speed is zero Enabled
13 S Software limit pin debouncing Enabled
14 R Parking override control Enabled
15 + Safety door input pin Enabled
16 * Restore all EEPROM command Disabled
17 $ Restore EEPROM `$` settings command Disabled
18 # Restore EEPROM parameter data command Disabled
19 I Build info write user string command Disabled
20 E Force sync upon EEPROM write Disabled
21 W Force sync upon work coordinate offset change Disabled
22 L Homing initialization auto-lock Disabled

View File

@ -0,0 +1,44 @@
"Error Code in v1.1+","Error Message in v1.0-","Error Description"
"1","Expected command letter","G-code words consist of a letter and a value. Letter was not found."
"2","Bad number format","Missing the expected G-code word value or numeric value format is not valid."
"3","Invalid statement","Grbl '$' system command was not recognized or supported."
"4","Value < 0","Negative value received for an expected positive value."
"5","Setting disabled","Homing cycle failure. Homing is not enabled via settings."
"6","Value < 3 usec","Minimum step pulse time must be greater than 3usec."
"7","EEPROM read fail. Using defaults","An EEPROM read failed. Auto-restoring affected EEPROM to default values."
"8","Not idle","Grbl '$' command cannot be used unless Grbl is IDLE. Ensures smooth operation during a job."
"9","G-code lock","G-code commands are locked out during alarm or jog state."
"10","Homing not enabled","Soft limits cannot be enabled without homing also enabled."
"11","Line overflow","Max characters per line exceeded. Received command line was not executed."
"12","Step rate > 30kHz","Grbl '$' setting value cause the step rate to exceed the maximum supported."
"13","Check Door","Safety door detected as opened and door state initiated."
"14","Line length exceeded","Build info or startup line exceeded EEPROM line length limit. Line not stored."
"15","Travel exceeded","Jog target exceeds machine travel. Jog command has been ignored."
"16","Invalid jog command","Jog command has no '=' or contains prohibited g-code."
"17","Setting disabled","Laser mode requires PWM output."
"20","Unsupported command","Unsupported or invalid g-code command found in block."
"21","Modal group violation","More than one g-code command from same modal group found in block."
"22","Undefined feed rate","Feed rate has not yet been set or is undefined."
"23","Invalid gcode ID:23","G-code command in block requires an integer value."
"24","Invalid gcode ID:24","More than one g-code command that requires axis words found in block."
"25","Invalid gcode ID:25","Repeated g-code word found in block."
"26","Invalid gcode ID:26","No axis words found in block for g-code command or current modal state which requires them."
"27","Invalid gcode ID:27","Line number value is invalid."
"28","Invalid gcode ID:28","G-code command is missing a required value word."
"29","Invalid gcode ID:29","G59.x work coordinate systems are not supported."
"30","Invalid gcode ID:30","G53 only allowed with G0 and G1 motion modes."
"31","Invalid gcode ID:31","Axis words found in block when no command or current modal state uses them."
"32","Invalid gcode ID:32","G2 and G3 arcs require at least one in-plane axis word."
"33","Invalid gcode ID:33","Motion command target is invalid."
"34","Invalid gcode ID:34","Arc radius value is invalid."
"35","Invalid gcode ID:35","G2 and G3 arcs require at least one in-plane offset word."
"36","Invalid gcode ID:36","Unused value words found in block."
"37","Invalid gcode ID:37","G43.1 dynamic tool length offset is not assigned to configured tool length axis."
"38","Invalid gcode ID:38","Tool number greater than max supported value."
"39","Parameter P exceeded max ID:39","Parameter P exceeded max"
"60","SD failed to mount"
"61","SD card failed to open file for reading"
"62","SD card failed to open directory"
"63","SD Card directory not found"
"64","SD Card file empty"
"70","Bluetooth failed to start"
Can't render this file because it has a wrong number of fields in line 39.

View File

@ -0,0 +1,46 @@
"$-Code"," Setting"," Units"," Setting Description"
"0","Step pulse time","microseconds","Sets time length per step. Minimum 3usec."
"1","Step idle delay","milliseconds","Sets a short hold delay when stopping to let dynamics settle before disabling steppers. Value 255 keeps motors enabled with no delay."
"2","Step pulse invert","mask","Inverts the step signal. Set axis bit to invert (00000ZYX)."
"3","Step direction invert","mask","Inverts the direction signal. Set axis bit to invert (00000ZYX)."
"4","Invert step enable pin","boolean","Inverts the stepper driver enable pin signal."
"5","Invert limit pins","boolean","Inverts the all of the limit input pins."
"6","Invert probe pin","boolean","Inverts the probe input pin signal."
"10","Status report options","mask","Alters data included in status reports."
"11","Junction deviation","millimeters","Sets how fast Grbl travels through consecutive motions. Lower value slows it down."
"12","Arc tolerance","millimeters","Sets the G2 and G3 arc tracing accuracy based on radial error. Beware: A very small value may effect performance."
"13","Report in inches","boolean","Enables inch units when returning any position and rate value that is not a settings value."
"20","Soft limits enable","boolean","Enables soft limits checks within machine travel and sets alarm when exceeded. Requires homing."
"21","Hard limits enable","boolean","Enables hard limits. Immediately halts motion and throws an alarm when switch is triggered."
"22","Homing cycle enable","boolean","Enables homing cycle. Requires limit switches on all axes."
"23","Homing direction invert","mask","Homing searches for a switch in the positive direction. Set axis bit (00000ZYX) to search in negative direction."
"24","Homing locate feed rate","mm/min","Feed rate to slowly engage limit switch to determine its location accurately."
"25","Homing search seek rate","mm/min","Seek rate to quickly find the limit switch before the slower locating phase."
"26","Homing switch debounce delay","milliseconds","Sets a short delay between phases of homing cycle to let a switch debounce."
"27","Homing switch pull-off distance","millimeters","Retract distance after triggering switch to disengage it. Homing will fail if switch isn't cleared."
"30","Maximum spindle speed","RPM","Maximum spindle speed. Sets PWM to 100% duty cycle."
"31","Minimum spindle speed","RPM","Minimum spindle speed. Sets PWM to 0.4% or lowest duty cycle."
"32","Laser-mode enable","boolean","Enables laser mode. Consecutive G1/2/3 commands will not halt when spindle speed is changed."
"33","Spindle PWM Freq","16-bit","Spindle PWM Freq"
"34","Spindle PWM Off Value","16-bit","Spindle PWM Off Value"
"35","Spindle PWM Min Value","16-bit","Spindle PWM Min Value"
"36","Spindle PWM Max Value","16-bit","Spindle PWM Max Value"
"80-84","User integer Values","unsigned 16-bit","Reserved for custom machine use"
"90-94","User Floating point value","float","Reserved for custom machine use"
"100","X-axis travel resolution","step/mm","X-axis travel resolution in steps per millimeter."
"101","Y-axis travel resolution","step/mm","Y-axis travel resolution in steps per millimeter."
"102","Z-axis travel resolution","step/mm","Z-axis travel resolution in steps per millimeter."
"110","X-axis maximum rate","mm/min","X-axis maximum rate. Used as G0 rapid rate."
"111","Y-axis maximum rate","mm/min","Y-axis maximum rate. Used as G0 rapid rate."
"112","Z-axis maximum rate","mm/min","Z-axis maximum rate. Used as G0 rapid rate."
"120","X-axis acceleration","mm/sec^2","X-axis acceleration. Used for motion planning to not exceed motor torque and lose steps."
"121","Y-axis acceleration","mm/sec^2","Y-axis acceleration. Used for motion planning to not exceed motor torque and lose steps."
"122","Z-axis acceleration","mm/sec^2","Z-axis acceleration. Used for motion planning to not exceed motor torque and lose steps."
"130","X-axis maximum travel","millimeters","Maximum X-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances."
"131","Y-axis maximum travel","millimeters","Maximum Y-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances."
"132","Z-axis maximum travel","millimeters","Maximum Z-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances."
"140-145","Motor run current","Amps","Motor run current for SPI (Trinamic) type motors"
"150-155","Motor hold current","Percent","Hold current in percent of run current for SPI (Trinamic) type motors"
"160-165","Motor microstepping","micros/step","Number of microsteps per step for SPI (Trinamic) type motors"
"170-175","Motor Stallguard Value","0-255","Value of Stallguard setting for SPI (Trinamic) type motors"
1 $-Code Setting Units Setting Description
2 0 Step pulse time microseconds Sets time length per step. Minimum 3usec.
3 1 Step idle delay milliseconds Sets a short hold delay when stopping to let dynamics settle before disabling steppers. Value 255 keeps motors enabled with no delay.
4 2 Step pulse invert mask Inverts the step signal. Set axis bit to invert (00000ZYX).
5 3 Step direction invert mask Inverts the direction signal. Set axis bit to invert (00000ZYX).
6 4 Invert step enable pin boolean Inverts the stepper driver enable pin signal.
7 5 Invert limit pins boolean Inverts the all of the limit input pins.
8 6 Invert probe pin boolean Inverts the probe input pin signal.
9 10 Status report options mask Alters data included in status reports.
10 11 Junction deviation millimeters Sets how fast Grbl travels through consecutive motions. Lower value slows it down.
11 12 Arc tolerance millimeters Sets the G2 and G3 arc tracing accuracy based on radial error. Beware: A very small value may effect performance.
12 13 Report in inches boolean Enables inch units when returning any position and rate value that is not a settings value.
13 20 Soft limits enable boolean Enables soft limits checks within machine travel and sets alarm when exceeded. Requires homing.
14 21 Hard limits enable boolean Enables hard limits. Immediately halts motion and throws an alarm when switch is triggered.
15 22 Homing cycle enable boolean Enables homing cycle. Requires limit switches on all axes.
16 23 Homing direction invert mask Homing searches for a switch in the positive direction. Set axis bit (00000ZYX) to search in negative direction.
17 24 Homing locate feed rate mm/min Feed rate to slowly engage limit switch to determine its location accurately.
18 25 Homing search seek rate mm/min Seek rate to quickly find the limit switch before the slower locating phase.
19 26 Homing switch debounce delay milliseconds Sets a short delay between phases of homing cycle to let a switch debounce.
20 27 Homing switch pull-off distance millimeters Retract distance after triggering switch to disengage it. Homing will fail if switch isn't cleared.
21 30 Maximum spindle speed RPM Maximum spindle speed. Sets PWM to 100% duty cycle.
22 31 Minimum spindle speed RPM Minimum spindle speed. Sets PWM to 0.4% or lowest duty cycle.
23 32 Laser-mode enable boolean Enables laser mode. Consecutive G1/2/3 commands will not halt when spindle speed is changed.
24 33 Spindle PWM Freq 16-bit Spindle PWM Freq
25 34 Spindle PWM Off Value 16-bit Spindle PWM Off Value
26 35 Spindle PWM Min Value 16-bit Spindle PWM Min Value
27 36 Spindle PWM Max Value 16-bit Spindle PWM Max Value
28 80-84 User integer Values unsigned 16-bit Reserved for custom machine use
29 90-94 User Floating point value float Reserved for custom machine use
30 100 X-axis travel resolution step/mm X-axis travel resolution in steps per millimeter.
31 101 Y-axis travel resolution step/mm Y-axis travel resolution in steps per millimeter.
32 102 Z-axis travel resolution step/mm Z-axis travel resolution in steps per millimeter.
33 110 X-axis maximum rate mm/min X-axis maximum rate. Used as G0 rapid rate.
34 111 Y-axis maximum rate mm/min Y-axis maximum rate. Used as G0 rapid rate.
35 112 Z-axis maximum rate mm/min Z-axis maximum rate. Used as G0 rapid rate.
36 120 X-axis acceleration mm/sec^2 X-axis acceleration. Used for motion planning to not exceed motor torque and lose steps.
37 121 Y-axis acceleration mm/sec^2 Y-axis acceleration. Used for motion planning to not exceed motor torque and lose steps.
38 122 Z-axis acceleration mm/sec^2 Z-axis acceleration. Used for motion planning to not exceed motor torque and lose steps.
39 130 X-axis maximum travel millimeters Maximum X-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances.
40 131 Y-axis maximum travel millimeters Maximum Y-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances.
41 132 Z-axis maximum travel millimeters Maximum Z-axis travel distance from homing switch. Determines valid machine space for soft-limits and homing search distances.
42 140-145 Motor run current Amps Motor run current for SPI (Trinamic) type motors
43 150-155 Motor hold current Percent Hold current in percent of run current for SPI (Trinamic) type motors
44 160-165 Motor microstepping micros/step Number of microsteps per step for SPI (Trinamic) type motors
45 170-175 Motor Stallguard Value 0-255 Value of Stallguard setting for SPI (Trinamic) type motors

View File

@ -0,0 +1,373 @@
"""
---------------------
The MIT License (MIT)
Copyright (c) 2017-2018 Sungeun K. Jeon for Gnea Research LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---------------------
"""
"""
This Python script produces a continuous piece-wise line fit of actual spindle speed over
programmed speed/PWM, which must be measured and provided by the user. A plot of the data
and line fit will be auto-generated and saved in the working directory as 'line_fit.png'.
REQUIREMENTS:
- Python 2.7 or 3.x with SciPy, NumPy, and Matplotlib Python Libraries
- For the most people, the easiest way to run this script is on the free cloud service
https://repl.it/site/languages/python3. No account necessary. Unlimited runs.
- Last checked on 4/5/2018. This site has been regularly evolving and becoming more
powerful. Things may not work exactly as shown. Please report any issues.
- To use, go to the website and start the Python REPL. Copy and paste this script into
the main.py file in the browser editor. Click 'Run' and a solution should appear in
the text output in the REPL console window. You can edit the script directly in the
browser and re-run the script as many times as you need. A free account is only
necessary if you want to save files on their servers.
- This script will also automatically generate a png image with the plot of the data
with the piece-wise linear fit over it, but this will not show up by default on this
website. To enable this, just click the 'Add File' icon and create a dummy file.
Name it anything, like dummy.py. Leave this file blank. Doing this places the REPL
in multiple file mode and will enable viewing the plot. Click the 'Run' icon. The
solution will be presented again in the console, and the data plot will appear in
the file list called 'line_fit.png'. Click the file to view the plot.
- For offline Python installs, most Mac and Linux computers have Python pre-installed
with the required libraries. If not, a quick google search will show you how to
install them. For Windows, Python installations are bit more difficult. Anaconda and
Pyzo seem to work well.
USAGE:
- First, make sure you are using the stock build of Grbl for the 328p processor. Most
importantly, the SPINDLE_PWM_MAX_VALUE and SPINDLE_PWM_MIN_VALUE should be unaltered
from defaults, otherwise change them back to 255.0 and 1.0 respectively for this test.
- Next, program the max and min rpm Grbl settings to '$30=255' and '$31=1'. This sets
the internal PWM values equal to 'S' spindle speed for the standard Grbl build.
- Check if your spindle does not turn on at very low voltages by setting 'S' spindle
speed to 'S1'. If it does not turn on or turns at a non-useful rpm, increase 'S' by
one until it does. Write down this 'S' value for later. You'll start the rpm data
collection from this point onward and will need to update the SPINDLE_PWM_MIN_VALUE
in cpu_map.h afterwards.
- Collect actual spindle speed with a tachometer or similar means over a range of 'S'
and PWM values. Start by setting the spindle 'S' speed to the minimum useful 'S' from
the last step and measure and record actual spindle rpm. Next, increase 'S' spindle
speed over equally sized intervals and repeat the measurement. Increments of 20 rpm
should be more than enough, but decrease increment size, if highly nonlinear. Complete
the data collection the 'S' spindle speed equal to '$30' max rpm, or at the max useful
rpm, and record the actual rpm output. Make sure to collect rpm data all the way
throughout useful output rpm. The actual operating range within this model may be set
later within Grbl with the '$30' and '$31' settings.
- In some cases, spindle PWM output can have discontinuities or not have a useful rpm
in certain ranges. For example, a known controller board has the spindle rpm drop
completely at voltages above ~4.5V. If you have discontinuities like this at the low
or high range of rpm, simply trim them from the data set. Don't include them. For
Grbl to compensate, you'll need to alter the SPINDLE_PWM_MIN_VALUE and/or
SPINDLE_PWM_MAX_VALUE in cpu_map.h to where your data set ends. This script will
indicate if you need to do that in the solution output.
- Keep in mind that spindles without control electronics can slow down drastically when
cutting and under load. How much it slows down is dependent on a lot of factors, such
as feed rate, chip load, cutter diameter, flutes, cutter type, lubricant/coolant,
material being cut, etc. Even spindles with controllers can still slow down if the
load is higher than the max current the controller can provide. It's recommended to
frequently re-check and measure actual spindle speed during a job. You can always use
spindle speed overrides to tweak it temporarily to the desired speed.
- Edit this script and enter the measured rpm values and their corresponding 'S' spindle
speed values in the data arrays below. Set the number of piecewise lines you would
like to use, from one to four lines. For most cases, four lines is perfectly fine.
In certain scenarios (laser engraving), this may significantly degrade performance and
should be reduced if possible.
- Run the Python script. Visually assess the line fit from the plot. It will not likely
to what you want on the first go. Dial things in by altering the line fit junction
points 'PWM_pointX' in this script to move where the piecewise line junctions are
located along the plot x-axis. It may be desired to tweak the junction points so the
model solution is more accurate in the region that the spindle typically running.
Re-run the script and tweak the junction points until you are satified with the model.
- Record the solution and enter the RPM_POINT and RPM_LINE values into config.h. Set the
number of piecewise lines used in this model in config.h. Also set the '$30' and '$31'
max and min rpm values to the solution values or in a range between them in Grbl '$'
settings. And finally, alter the SPINDLE_PWM_MIN_VALUE in cpu_map.h, if your spindle
needs to be above a certain voltage to produce a useful low rpm.
- Once the solution is entered. Recompile and flash Grbl. This solution model is only
valid for this particular set of data. If the machine is altered, you will need to
perform this experiment again and regenerate a new model here.
OUTPUT:
The solver produces a set of values that define the piecewise fit and can be used by
Grbl to quickly and efficiently compute spindle PWM output voltage for a desired RPM.
The first two are the RPM_MAX ($30) and RPM_MIN ($31) Grbl settings. These must be
programmed into Grbl manually or setup in defaults.h for new systems. Altering these
values within Grbl after a piece-wise linear model is installed will not change alter
model. It will only alter the range of spindle speed rpm values Grbl output.
For example, if the solver produces an RPM_MAX of 9000 and Grbl is programmed with
$30=8000, S9000 may be programmed, but Grbl will only produce the output voltage to run
at 8000 rpm. In other words, Grbl will only output voltages the range between
max(RPM_MIN,$31) and min(RPM_MAX,$30).
The remaining values define the slopes and offsets of the line segments and the junction
points between line segments, like so for n_pieces=3:
PWM_output = RPM_LINE_A1 * rpm - RPM_LINE_B1 [ RPM_MIN < rpm < RPM_POINT12 ]
PWM_output = RPM_LINE_A2 * rpm - RPM_LINE_B2 [ RPM_POINT12 < rpm < RPM_POINT23 ]
PWM_output = RPM_LINE_A3 * rpm - RPM_LINE_B3 [ RPM_POINT23 < rpm < RPM_MAX ]
NOTE: The script solves in terms of PWM but the final equations and values are expressed
in terms of rpm in the form 'PWM = a*rpm - b'.
"""
from scipy import optimize
import numpy as np
# ----------------------------------------------------------------------------------------
# Configure spindle PWM line fit solver
n_pieces = 4 # Number of line segments used for data fit. Only 1 to 4 line segments supported.
# Programmed 'S' spindle speed values. Must start with minimum useful PWM or 'S' programmed
# value and end with the maximum useful PWM or 'S' programmed value. Order of the array must
# be synced with the RPM_measured array below.
# NOTE: ** DO NOT USE DATA FROM AN EXISTING PIECEWISE LINE FIT. USE DEFAULT GRBL MODEL ONLY. **
PWM_set = np.array([2,18,36,55,73,91,109,127,146,164,182,200,218,237,254], dtype=float)
# Actual RPM measured at the spindle. Must be in the ascending value and equal in length
# as the PWM_set array. Must include the min and max measured rpm output in the first and
# last array entries, respectively.
RPM_measured = np.array([213.,5420,7145,8282,9165,9765,10100,10500,10700,10900,11100,11250,11400,11550,11650], dtype=float)
# Configure line fit points by 'S' programmed rpm or PWM value. Values must be between
# PWM_max and PWM_min. Typically, alter these values to space the points evenly between
# max and min PWM range. However, they may be tweaked to maximize accuracy in the places
# you normally operate for highly nonlinear curves. Plot to visually assess how well the
# solution fits the data.
PWM_point1 = 20.0 # (S) Point between segments 0 and 1. Used when n_pieces >= 2.
PWM_point2 = 80.0 # (S) Point between segments 1 and 2. Used when n_pieces >= 3.
PWM_point3 = 150.0 # (S) Point between segments 2 and 3. Used when n_pieces = 4.
# ----------------------------------------------------------------------------------------
# Advanced settings
# The optimizer requires an initial guess of the solution. Change value if solution fails.
slope_i = 100.0; # > 0.0
PWM_max = max(PWM_set) # Maximum PWM set in measured range
PWM_min = min(PWM_set) # Minimum PWM set in measured range
plot_figure = True # Set to False, if matplotlib is not available.
# ----------------------------------------------------------------------------------------
# DO NOT ALTER ANYTHING BELOW.
def piecewise_linear_1(x,b,k1):
return np.piecewise(x, [(x>=PWM_min)&(x<=PWM_max)], [lambda x:k1*(x-PWM_min)+b])
def piecewise_linear_2(x,b,k1,k2):
c = [b,
b+k1*(PWM_point1-PWM_min)]
funcs = [lambda x:k1*(x-PWM_min)+c[0],
lambda x:k2*(x-PWM_point1)+c[1]]
conds = [(x<PWM_point1)&(x>=PWM_min),
(x<=PWM_max)&(x>=PWM_point1)]
return np.piecewise(x, conds, funcs)
def piecewise_linear_3(x,b,k1,k2,k3):
c = [b,
b+k1*(PWM_point1-PWM_min),
b+k1*(PWM_point1-PWM_min)+k2*(PWM_point2-PWM_point1)]
funcs = [lambda x:k1*(x-PWM_min)+c[0],
lambda x:k2*(x-PWM_point1)+c[1],
lambda x:k3*(x-PWM_point2)+c[2]]
conds = [(x<PWM_point1)&(x>=PWM_min),
(x<PWM_point2)&(x>=PWM_point1),
(x<=PWM_max)&(x>=PWM_point2)]
return np.piecewise(x, conds, funcs)
def piecewise_linear_4(x,b,k1,k2,k3,k4):
c = [b,
b+k1*(PWM_point1-PWM_min),
b+k1*(PWM_point1-PWM_min)+k2*(PWM_point2-PWM_point1),
b+k1*(PWM_point1-PWM_min)+k2*(PWM_point2-PWM_point1)+k3*(PWM_point3-PWM_point2)]
funcs = [lambda x:k1*(x-PWM_min)+c[0],
lambda x:k2*(x-PWM_point1)+c[1],
lambda x:k3*(x-PWM_point2)+c[2],
lambda x:k4*(x-PWM_point3)+c[3]]
conds = [(x<PWM_point1)&(x>=PWM_min),
(x<PWM_point2)&(x>=PWM_point1),
(x<PWM_point3)&(x>=PWM_point2),
(x<=PWM_max)&(x>=PWM_point3)]
return np.piecewise(x, conds, funcs)
# ----------------------------------------------------------------------------------------
print("\nCONFIG:")
print(" N_pieces: %i" % n_pieces)
print(" PWM_min: %.1f" % PWM_min)
print(" PWM_max: %.1f" % PWM_max)
if n_pieces > 1:
print(" PWM_point1: %.1f" % PWM_point1)
if n_pieces > 2:
print(" PWM_point2: %.1f" % PWM_point2)
if n_pieces > 3:
print(" PWM_point3: %.1f" % PWM_point3)
print(" N_data: %i" % len(RPM_measured))
print(" PWM_set: ", PWM_set)
print(" RPM_measured: ", RPM_measured)
if n_pieces == 1:
piece_func = piecewise_linear_1
p_initial = [RPM_measured[0],slope_i]
p , e = optimize.curve_fit(piece_func, PWM_set, RPM_measured, p0=p_initial)
a = [p[1]]
b = [ p[0]-p[1]*PWM_min]
rpm = [ p[0],
p[0]+p[1]*(PWM_point1-PWM_min)]
elif n_pieces == 2:
piece_func = piecewise_linear_2
p_initial = [RPM_measured[0],slope_i,slope_i]
p , e = optimize.curve_fit(piece_func, PWM_set, RPM_measured, p0=p_initial)
a = [p[1],p[2]]
b = [ p[0]-p[1]*PWM_min,
p[0]+p[1]*(PWM_point1-PWM_min)-p[2]*PWM_point1]
rpm = [ p[0],
p[0]+p[1]*(PWM_point1-PWM_min),
p[0]+p[1]*(PWM_point1-PWM_min)+p[2]*(PWM_max-PWM_point1)]
elif n_pieces == 3:
piece_func = piecewise_linear_3
p_initial = [RPM_measured[0],slope_i,slope_i,slope_i]
p , e = optimize.curve_fit(piece_func, PWM_set, RPM_measured, p0=p_initial)
a = [p[1],p[2],p[3]]
b = [ p[0]-p[1]*PWM_min,
p[0]+p[1]*(PWM_point1-PWM_min)-p[2]*PWM_point1,
p[0]+p[1]*(PWM_point1-PWM_min)+p[2]*(PWM_point2-PWM_point1)-p[3]*PWM_point2]
rpm = [ p[0],
p[0]+p[1]*(PWM_point1-PWM_min),
p[0]+p[1]*(PWM_point1-PWM_min)+p[2]*(PWM_point2-PWM_point1),
p[0]+p[1]*(PWM_point1-PWM_min)+p[2]*(PWM_point2-PWM_point1)+p[3]*(PWM_max-PWM_point2) ]
elif n_pieces == 4:
piece_func = piecewise_linear_4
p_initial = [RPM_measured[0],slope_i,slope_i,slope_i,slope_i]
p , e = optimize.curve_fit(piece_func, PWM_set, RPM_measured, p0=p_initial)
a = [p[1],p[2],p[3],p[4]]
b = [ p[0]-p[1]*PWM_min,
p[0]+p[1]*(PWM_point1-PWM_min)-p[2]*PWM_point1,
p[0]+p[1]*(PWM_point1-PWM_min)+p[2]*(PWM_point2-PWM_point1)-p[3]*PWM_point2,
p[0]+p[1]*(PWM_point1-PWM_min)+p[2]*(PWM_point2-PWM_point1)+p[3]*(PWM_point3-PWM_point2)-p[4]*PWM_point3 ]
rpm = [ p[0],
p[0]+p[1]*(PWM_point1-PWM_min),
p[0]+p[1]*(PWM_point1-PWM_min)+p[2]*(PWM_point2-PWM_point1),
p[0]+p[1]*(PWM_point1-PWM_min)+p[2]*(PWM_point2-PWM_point1)+p[3]*(PWM_point3-PWM_point2),
p[0]+p[1]*(PWM_point1-PWM_min)+p[2]*(PWM_point2-PWM_point1)+p[3]*(PWM_point3-PWM_point2)+p[4]*(PWM_max-PWM_point3) ]
else :
print("ERROR: Unsupported number of pieces. Check and alter n_pieces")
quit()
print("\nSOLUTION:\n\n[Update these #define values and uncomment]\n[ENABLE_PIECEWISE_LINEAR_SPINDLE in config.h.]")
print("#define N_PIECES %.0f" % n_pieces)
print("#define RPM_MAX %.1f" % rpm[-1])
print("#define RPM_MIN %.1f" % rpm[0])
if n_pieces > 1:
print("#define RPM_POINT12 %.1f" % rpm[1])
if n_pieces > 2:
print("#define RPM_POINT23 %.1f" %rpm[2])
if n_pieces > 3:
print("#define RPM_POINT34 %.1f" %rpm[3])
print("#define RPM_LINE_A1 %.6e" % (1./a[0]))
print("#define RPM_LINE_B1 %.6e" % (b[0]/a[0]))
if n_pieces > 1:
print("#define RPM_LINE_A2 %.6e" % (1./a[1]))
print("#define RPM_LINE_B2 %.6e" % (b[1]/a[1]))
if n_pieces > 2:
print("#define RPM_LINE_A3 %.6e" % (1./a[2]))
print("#define RPM_LINE_B3 %.6e" % (b[2]/a[2]))
if n_pieces > 3:
print("#define RPM_LINE_A4 %.6e" % (1./a[3]))
print("#define RPM_LINE_B4 %.6e" % (b[3]/a[3]))
print("\n[To operate over full model range, manually write these]")
print("['$' settings or alter values in defaults.h. Grbl will]")
print("[operate between min($30,RPM_MAX) and max($31,RPM_MIN)]")
print("$30=%.1f (rpm max)" % rpm[-1])
print("$31=%.1f (rpm min)" % rpm[0])
if (PWM_min > 1)|(PWM_max<255):
print("\n[Update the following #define values in cpu_map.h]")
if (PWM_min >1) :
print("#define SPINDLE_PWM_MIN_VALUE %.0f" % PWM_min)
if PWM_max <255:
print("#define SPINDLE_PWM_MAX_VALUE %.0f" % PWM_max)
else:
print("\n[No cpu_map.h changes required.]")
print("\n")
test_val = (1./a[0])*rpm[0] - (b[0]/a[0])
if test_val < 0.0 :
print("ERROR: Solution is negative at RPM_MIN. Adjust junction points or increase n_pieces.\n")
if plot_figure:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
xd = np.linspace(PWM_min, PWM_max, 10000)
ax.plot(PWM_set, RPM_measured, "o")
ax.plot(xd, piece_func(xd, *p),'g')
plt.xlabel("Programmed PWM")
plt.ylabel("Measured RPM")
# Check solution by plotting in terms of rpm.
# x = np.linspace(rpm[0], rpm[1], 10000)
# ax.plot((1./a[0])*x-(b[0]/a[0]),x,'r:')
# if n_pieces > 1:
# x = np.linspace(rpm[1], rpm[2], 10000)
# ax.plot((1./a[1])*x-(b[1]/a[1]),x,'r:')
# if n_pieces > 2:
# x = np.linspace(rpm[2], rpm[3], 10000)
# ax.plot((1./a[2])*x-(b[2]/a[2]),x,'r:')
# if n_pieces > 3:
# x = np.linspace(rpm[3], rpm[-1], 10000)
# ax.plot((1./a[3])*x-(b[3]/a[3]),x,'r:')
fig.savefig("line_fit.png")

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,66 @@
#!/usr/bin/env python
"""\
Simple g-code streaming script for grbl
Provided as an illustration of the basic communication interface
for grbl. When grbl has finished parsing the g-code block, it will
return an 'ok' or 'error' response. When the planner buffer is full,
grbl will not send a response until the planner buffer clears space.
G02/03 arcs are special exceptions, where they inject short line
segments directly into the planner. So there may not be a response
from grbl for the duration of the arc.
---------------------
The MIT License (MIT)
Copyright (c) 2012 Sungeun K. Jeon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---------------------
"""
import serial
import time
# Open grbl serial port
s = serial.Serial('/dev/tty.usbmodem1811',115200)
# Open g-code file
f = open('grbl.gcode','r');
# Wake up grbl
s.write("\r\n\r\n")
time.sleep(2) # Wait for grbl to initialize
s.flushInput() # Flush startup text in serial input
# Stream g-code to grbl
for line in f:
l = line.strip() # Strip all EOL characters for consistency
print 'Sending: ' + l,
s.write(l + '\n') # Send g-code block to grbl
grbl_out = s.readline() # Wait for grbl response with carriage return
print ' : ' + grbl_out.strip()
# Wait here until grbl is finished to close serial port and file.
raw_input(" Press <Enter> to exit and disable grbl.")
# Close file and serial port
f.close()
s.close()

View File

@ -0,0 +1,202 @@
#!/usr/bin/env python
"""\
Stream g-code to grbl controller
This script differs from the simple_stream.py script by
tracking the number of characters in grbl's serial read
buffer. This allows grbl to fetch the next line directly
from the serial buffer and does not have to wait for a
response from the computer. This effectively adds another
buffer layer to prevent buffer starvation.
CHANGELOG:
- 20170531: Status report feedback at 1.0 second intervals.
Configurable baudrate and report intervals. Bug fixes.
- 20161212: Added push message feedback for simple streaming
- 20140714: Updated baud rate to 115200. Added a settings
write mode via simple streaming method. MIT-licensed.
TODO:
- Add realtime control commands during streaming.
---------------------
The MIT License (MIT)
Copyright (c) 2012-2017 Sungeun K. Jeon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---------------------
"""
import serial
import re
import time
import sys
import argparse
import threading
RX_BUFFER_SIZE = 128
BAUD_RATE = 115200
ENABLE_STATUS_REPORTS = True
REPORT_INTERVAL = 1.0 # seconds
is_run = True # Controls query timer
# Define command line argument interface
parser = argparse.ArgumentParser(description='Stream g-code file to grbl. (pySerial and argparse libraries required)')
parser.add_argument('gcode_file', type=argparse.FileType('r'),
help='g-code filename to be streamed')
parser.add_argument('device_file',
help='serial device path')
parser.add_argument('-q','--quiet',action='store_true', default=False,
help='suppress output text')
parser.add_argument('-s','--settings',action='store_true', default=False,
help='settings write mode')
parser.add_argument('-c','--check',action='store_true', default=False,
help='stream in check mode')
args = parser.parse_args()
# Periodic timer to query for status reports
# TODO: Need to track down why this doesn't restart consistently before a release.
def send_status_query():
s.write('?')
def periodic_timer() :
while is_run:
send_status_query()
time.sleep(REPORT_INTERVAL)
# Initialize
s = serial.Serial(args.device_file,BAUD_RATE)
f = args.gcode_file
verbose = True
if args.quiet : verbose = False
settings_mode = False
if args.settings : settings_mode = True
check_mode = False
if args.check : check_mode = True
# Wake up grbl
print "Initializing Grbl..."
s.write("\r\n\r\n")
# Wait for grbl to initialize and flush startup text in serial input
time.sleep(2)
s.flushInput()
if check_mode :
print "Enabling Grbl Check-Mode: SND: [$C]",
s.write("$C\n")
while 1:
grbl_out = s.readline().strip() # Wait for grbl response with carriage return
if grbl_out.find('error') >= 0 :
print "REC:",grbl_out
print " Failed to set Grbl check-mode. Aborting..."
quit()
elif grbl_out.find('ok') >= 0 :
if verbose: print 'REC:',grbl_out
break
start_time = time.time();
# Start status report periodic timer
if ENABLE_STATUS_REPORTS :
timerThread = threading.Thread(target=periodic_timer)
timerThread.daemon = True
timerThread.start()
# Stream g-code to grbl
l_count = 0
error_count = 0
if settings_mode:
# Send settings file via simple call-response streaming method. Settings must be streamed
# in this manner since the EEPROM accessing cycles shut-off the serial interrupt.
print "SETTINGS MODE: Streaming", args.gcode_file.name, " to ", args.device_file
for line in f:
l_count += 1 # Iterate line counter
# l_block = re.sub('\s|\(.*?\)','',line).upper() # Strip comments/spaces/new line and capitalize
l_block = line.strip() # Strip all EOL characters for consistency
if verbose: print "SND>"+str(l_count)+": \"" + l_block + "\""
s.write(l_block + '\n') # Send g-code block to grbl
while 1:
grbl_out = s.readline().strip() # Wait for grbl response with carriage return
if grbl_out.find('ok') >= 0 :
if verbose: print " REC<"+str(l_count)+": \""+grbl_out+"\""
break
elif grbl_out.find('error') >= 0 :
if verbose: print " REC<"+str(l_count)+": \""+grbl_out+"\""
error_count += 1
break
else:
print " MSG: \""+grbl_out+"\""
else:
# Send g-code program via a more agressive streaming protocol that forces characters into
# Grbl's serial read buffer to ensure Grbl has immediate access to the next g-code command
# rather than wait for the call-response serial protocol to finish. This is done by careful
# counting of the number of characters sent by the streamer to Grbl and tracking Grbl's
# responses, such that we never overflow Grbl's serial read buffer.
g_count = 0
c_line = []
for line in f:
l_count += 1 # Iterate line counter
l_block = re.sub('\s|\(.*?\)','',line).upper() # Strip comments/spaces/new line and capitalize
# l_block = line.strip()
c_line.append(len(l_block)+1) # Track number of characters in grbl serial read buffer
grbl_out = ''
while sum(c_line) >= RX_BUFFER_SIZE-1 | s.inWaiting() :
out_temp = s.readline().strip() # Wait for grbl response
if out_temp.find('ok') < 0 and out_temp.find('error') < 0 :
print " MSG: \""+out_temp+"\"" # Debug response
else :
if out_temp.find('error') >= 0 : error_count += 1
g_count += 1 # Iterate g-code counter
if verbose: print " REC<"+str(g_count)+": \""+out_temp+"\""
del c_line[0] # Delete the block character count corresponding to the last 'ok'
s.write(l_block + '\n') # Send g-code block to grbl
if verbose: print "SND>"+str(l_count)+": \"" + l_block + "\""
# Wait until all responses have been received.
while l_count > g_count :
out_temp = s.readline().strip() # Wait for grbl response
if out_temp.find('ok') < 0 and out_temp.find('error') < 0 :
print " MSG: \""+out_temp+"\"" # Debug response
else :
if out_temp.find('error') >= 0 : error_count += 1
g_count += 1 # Iterate g-code counter
del c_line[0] # Delete the block character count corresponding to the last 'ok'
if verbose: print " REC<"+str(g_count)+": \""+out_temp + "\""
# Wait for user input after streaming is completed
print "\nG-code streaming finished!"
end_time = time.time();
is_run = False;
print " Time elapsed: ",end_time-start_time,"\n"
if check_mode :
if error_count > 0 :
print "CHECK FAILED:",error_count,"errors found! See output for details.\n"
else :
print "CHECK PASSED: No errors found in g-code program.\n"
else :
print "WARNING: Wait until Grbl completes buffered g-code blocks before exiting."
raw_input(" Press <Enter> to exit and disable Grbl.")
# Close file and serial port
f.close()
s.close()

View File

@ -0,0 +1,16 @@
cd %~dp0
cmd.exe /c npm install
cmd.exe /c npm audit fix
cmd.exe /c npm audit
cmd.exe /c gulp package
cmd.exe /c bin2c -o embedded.h -m tool.html.gz
cat header.txt > out.h
cat embedded.h >> out.h
cat footer.txt >> out.h
sed -i "s/tool_html_gz_size/PAGE_NOFILES_SIZE/g" ./out.h
sed -i "s/const unsigned char tool_html_gz/const char PAGE_NOFILES/g" ./out.h
sed -i "s/] = {/] PROGMEM = {/g" ./out.h
cat out.h > ../Grbl_Esp32/nofile.h
rm -f out.h
pause

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