diff --git a/Grbl_Esp32-master/.gitattributes b/Grbl_Esp32-master/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/Grbl_Esp32-master/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/bug-report.md b/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..a0a6a4b --- /dev/null +++ b/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/bug-report.md @@ -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 +``` diff --git a/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/feature_request.md b/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..44b5308 --- /dev/null +++ b/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/feature_request.md @@ -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?** diff --git a/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/general-discussion.md b/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/general-discussion.md new file mode 100644 index 0000000..6e1a488 --- /dev/null +++ b/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/general-discussion.md @@ -0,0 +1,10 @@ +--- +name: General Discussion +about: A general topic of interest to the group +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/problem-compiling-firmware.md b/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/problem-compiling-firmware.md new file mode 100644 index 0000000..0cb8a23 --- /dev/null +++ b/Grbl_Esp32-master/.github/ISSUE_TEMPLATE/problem-compiling-firmware.md @@ -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: diff --git a/Grbl_Esp32-master/.gitignore b/Grbl_Esp32-master/.gitignore new file mode 100644 index 0000000..0bea06f --- /dev/null +++ b/Grbl_Esp32-master/.gitignore @@ -0,0 +1,6 @@ +.pioenvs/ +Thumbs.db +.DS_Store +*.orig +embedded/node_modules +embedded/dist diff --git a/Grbl_Esp32-master/.travis.yml b/Grbl_Esp32-master/.travis.yml new file mode 100644 index 0000000..2e8c816 --- /dev/null +++ b/Grbl_Esp32-master/.travis.yml @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/BTconfig.cpp b/Grbl_Esp32-master/Grbl_Esp32/BTconfig.cpp new file mode 100644 index 0000000..46129e9 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/BTconfig.cpp @@ -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 +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/BTconfig.h b/Grbl_Esp32-master/Grbl_Esp32/BTconfig.h new file mode 100644 index 0000000..fd1cc50 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/BTconfig.h @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/Grbl_Esp32.ino b/Grbl_Esp32-master/Grbl_Esp32/Grbl_Esp32.ino new file mode 100644 index 0000000..1d4ee71 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/Grbl_Esp32.ino @@ -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 . +*/ + +#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(); + +} diff --git a/Grbl_Esp32-master/Grbl_Esp32/atari_1020.cpp b/Grbl_Esp32-master/Grbl_Esp32/atari_1020.cpp new file mode 100644 index 0000000..f26741e --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/atari_1020.cpp @@ -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 . + + -------------------------------------------------------------- + + 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 + diff --git a/Grbl_Esp32-master/Grbl_Esp32/atari_1020.h b/Grbl_Esp32-master/Grbl_Esp32/atari_1020.h new file mode 100644 index 0000000..94c0af9 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/atari_1020.h @@ -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 . + + 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< +#include "report.h" +#ifdef ENABLE_SD_CARD +#include "grbl_sd.h" +#endif +#ifdef ENABLE_BLUETOOTH +#include "BTconfig.h" +#endif +#ifdef ENABLE_WIFI +#include "wificonfig.h" +#if defined (ENABLE_HTTP) +#include "web_server.h" +#endif +#ifdef ENABLE_TELNET +#include "telnet_server.h" +#endif +#endif +#ifdef ENABLE_NOTIFICATIONS +#include "notifications_service.h" +#endif +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t esp_task_wdt_reset(); +#ifdef __cplusplus +} +#endif + +bool COMMANDS::restart_ESP_module = false; + +/* + * delay is to avoid with asyncwebserver and may need to wait sometimes + */ +void COMMANDS::wait(uint32_t milliseconds){ + uint32_t timeout = millis(); + esp_task_wdt_reset(); //for a wait 0; + //wait feeding WDT + while ( (millis() - timeout) < milliseconds) { + esp_task_wdt_reset(); + } +} + +bool COMMANDS::execute_internal_command (int cmd, String cmd_params, level_authenticate_type auth_level, ESPResponseStream *espresponse) +{ + bool response = true; + level_authenticate_type auth_type = auth_level; + if (!espresponse) return false; +#ifdef ENABLE_AUTHENTICATION + + if (isadmin(cmd_params)) { + auth_type = LEVEL_ADMIN; + } + if (isuser (cmd_params) && (auth_type != LEVEL_ADMIN) ) { + auth_type = LEVEL_USER; + } +#endif + //manage parameters + String parameter; + switch (cmd) { +#ifdef ENABLE_WIFI + //STA SSID + //[ESP100][pwd=] + case 100: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + String defV = DEFAULT_STA_SSID; + espresponse->println(prefs.getString(STA_SSID_ENTRY, defV).c_str()); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + if (!WiFiConfig::isSSIDValid (parameter.c_str() ) ) { + if(espresponse)espresponse->println ("Error: Incorrect SSID!"); + response = false; + } else { + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(STA_SSID_ENTRY, parameter) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + } + break; + //STA Password + //[ESP101][pwd=] + case 101: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if (!WiFiConfig::isPasswordValid (parameter.c_str() ) ) { + if(espresponse)espresponse->println ("Error: Incorrect password!"); + response = false; + return false; + } else { + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(STA_PWD_ENTRY, parameter) != parameter.length()){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + break; + //Change STA IP mode (DHCP/STATIC) + //[ESP102]pwd= + case 102: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + int8_t resp = prefs.getChar(STA_IP_MODE_ENTRY, DHCP_MODE); + if (resp == DHCP_MODE) espresponse->println("DHCP"); + else if (resp == STATIC_MODE) espresponse->println("STATIC"); + else espresponse->println("???"); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter.toUpperCase(); + if (!((parameter == "STATIC") || (parameter == "DHCP"))) { + if(espresponse)espresponse->println ("Error: only STATIC or DHCP mode supported!"); + response = false; + return false; + } else { + Preferences prefs; + prefs.begin(NAMESPACE, false); + int8_t bbuf = (parameter == "DHCP")?DHCP_MODE:STATIC_MODE; + if (prefs.putChar(STA_IP_MODE_ENTRY, bbuf) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + } + break; + //Change STA IP/Mask/GW + //[ESP103]IP= MSK= GW= pwd= + case 103: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + //IP + String defV = DEFAULT_STA_IP; + int32_t IP = prefs.getInt(STA_IP_ENTRY, wifi_config.IP_int_from_string(defV)); + //GW + defV = DEFAULT_STA_GW; + int32_t GW = prefs.getInt(STA_GW_ENTRY, wifi_config.IP_int_from_string(defV)); + //MK + defV = DEFAULT_STA_MK; + int32_t MK = prefs.getInt(STA_MK_ENTRY, wifi_config.IP_int_from_string(defV)); + defV = "IP:" + wifi_config.IP_string_from_int(IP) + ", GW:" + wifi_config.IP_string_from_int(GW) + ", MSK:" + wifi_config.IP_string_from_int(MK); + espresponse->println(defV.c_str()); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + + String IP = get_param (cmd_params, "IP=", false); + String GW = get_param (cmd_params, "GW=", false); + String MSK = get_param (cmd_params, "MSK=", false); + Serial.println(IP); + Serial.println(GW); + if ( !WiFiConfig::isValidIP(IP.c_str())) { + if(espresponse)espresponse->println ("Error: Incorrect IP!"); + response = false; + return false; + } + if ( !WiFiConfig::isValidIP(GW.c_str())) { + if(espresponse)espresponse->println ("Error: Incorrect Gateway!"); + response = false; + return false; + } + if ( !WiFiConfig::isValidIP(MSK.c_str())) { + if(espresponse)espresponse->println ("Error: Incorrect Mask!"); + response = false; + return false; + } + Preferences prefs; + prefs.begin(NAMESPACE, false); + if ((prefs.putInt(STA_IP_ENTRY, wifi_config.IP_int_from_string(IP)) == 0) || + (prefs.putInt(STA_GW_ENTRY, wifi_config.IP_int_from_string(GW)) == 0) || + (prefs.putInt(STA_MK_ENTRY, wifi_config.IP_int_from_string(MSK)) == 0)){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + break; + + //Change AP SSID + //[ESP105]pwd= + case 105: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + String defV = DEFAULT_AP_SSID; + espresponse->println(prefs.getString(AP_SSID_ENTRY, defV).c_str()); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + if (!WiFiConfig::isSSIDValid (parameter.c_str() ) ) { + if(espresponse)espresponse->println ("Error: Incorrect SSID!"); + response = false; + } + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(AP_SSID_ENTRY, parameter) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + break; + //Change AP Password + //[ESP106]pwd= + case 106: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if (!WiFiConfig::isPasswordValid (parameter.c_str() ) ) { + if(espresponse)espresponse->println ("Error: Incorrect password!"); + response = false; + return false; + } + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(AP_PWD_ENTRY, parameter) != parameter.length()){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + + } + break; + //Change AP IP + //[ESP107]pwd= + case 107: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + //IP + String defV = DEFAULT_AP_IP; + int32_t IP = prefs.getInt(AP_IP_ENTRY, wifi_config.IP_int_from_string(defV)); + espresponse->println(wifi_config.IP_string_from_int(IP).c_str()); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + if ( !WiFiConfig::isValidIP(parameter.c_str())) { + if(espresponse)espresponse->println ("Error: Incorrect IP!"); + response = false; + return false; + } + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putInt(AP_IP_ENTRY, wifi_config.IP_int_from_string(parameter)) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + break; + //Change AP channel + //[ESP108]pwd= + case 108: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + int8_t channel = prefs.getChar(AP_CHANNEL_ENTRY, DEFAULT_AP_CHANNEL); + espresponse->println(String(channel).c_str()); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + int8_t bbuf = parameter.toInt(); + if ((bbuf > MAX_CHANNEL) || (bbuf < MIN_CHANNEL)) { + if(espresponse)espresponse->println ("Error: Incorrect channel!"); + response = false; + return false; + } + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putChar(AP_CHANNEL_ENTRY, bbuf) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + break; +#endif +#if defined( ENABLE_WIFI) || defined( ENABLE_BLUETOOTH) + //Set radio state at boot which can be BT, STA, AP, OFF + //[ESP110]pwd= + case 110: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + int8_t wifiMode = prefs.getChar(ESP_RADIO_MODE, DEFAULT_RADIO_MODE); + if (wifiMode == ESP_RADIO_OFF) espresponse->println("OFF"); + else if (wifiMode == ESP_BT) espresponse->println("BT"); + else if (wifiMode == ESP_WIFI_AP) espresponse->println("AP"); + else if (wifiMode == ESP_WIFI_STA) espresponse->println("STA"); + else espresponse->println("??"); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter.toUpperCase(); + if (!( +#if defined( ENABLE_BLUETOOTH) + (parameter == "BT") || +#endif +#if defined( ENABLE_WIFI) + (parameter == "STA") || (parameter == "AP") || +#endif + (parameter == "OFF"))) { + + if(espresponse)espresponse->println ("Error: only " +#ifdef ENABLE_BLUETOOTH + "BT or " +#endif +#ifdef ENABLE_WIFI + "STA or AP or " +#endif + "OFF mode supported!"); + response = false; + return false; + } else { + Preferences prefs; + prefs.begin(NAMESPACE, false); + int8_t bbuf = ESP_RADIO_OFF; +#ifdef ENABLE_WIFI + if(parameter == "STA")bbuf = ESP_WIFI_STA; + if(parameter == "AP")bbuf = ESP_WIFI_AP; +#endif +#ifdef ENABLE_BLUETOOTH + if(parameter == "BT")bbuf = ESP_BT; +#endif + if (prefs.putChar(ESP_RADIO_MODE, bbuf) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + } + break; +#endif +#ifdef ENABLE_WIFI + //Get current IP + //[ESP111]
+ case 111: + { + if (!espresponse) return false; + String currentIP = cmd_params; + if (WiFi.getMode() == WIFI_STA) { + currentIP += WiFi.localIP().toString(); + } else { + currentIP += WiFi.softAPIP().toString(); + } + espresponse->println (currentIP.c_str()); + } + break; + + //Get/Set hostname + //[ESP112] pwd= + case 112: { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //Get hostname + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + String defV = DEFAULT_HOSTNAME; + espresponse->println(prefs.getString(HOSTNAME_ENTRY, defV).c_str()); + prefs.end(); + } else { //set host name +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + if (!wifi_config.isHostnameValid (parameter.c_str() ) ) { + if(espresponse)espresponse->println ("Error: Incorrect hostname!"); + response = false; + } else { + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(HOSTNAME_ENTRY, parameter) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + } + break; +#endif +#if defined (ENABLE_WIFI) || defined (ENABLE_BLUETOOTH) + //Set immediate radio state which can be ON, OFF + //[ESP115]pwd= + case 115:{ +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + //get + if (parameter.length() == 0) { + bool on =false; +#if defined (ENABLE_WIFI) + if (WiFi.getMode() != WIFI_MODE_NULL)on = true; +#endif +#if defined (ENABLE_BLUETOOTH) + if (bt_config.Is_BT_on())on = true; +#endif + espresponse->println ((on)?"ON":"OFF"); + } else { + parameter.toUpperCase(); + if (!((parameter == "ON") || (parameter == "OFF"))) { + if(espresponse)espresponse->println ("Error: only ON or OFF mode supported!"); + return false; + } else { + //Stop everything +#if defined (ENABLE_WIFI) + if (WiFi.getMode() != WIFI_MODE_NULL)wifi_config.StopWiFi(); +#endif +#if defined (ENABLE_BLUETOOTH) + if (bt_config.Is_BT_on())bt_config.end(); +#endif + + //if On start proper service + if (parameter == "ON") { //On + Preferences prefs; + prefs.begin(NAMESPACE, true); + int8_t wifiMode = prefs.getChar(ESP_RADIO_MODE, DEFAULT_RADIO_MODE); + prefs.end(); + if ((wifiMode == ESP_WIFI_AP) || (wifiMode == ESP_WIFI_STA)){ +#if defined (ENABLE_WIFI) + wifi_config.begin(); +#else + if(espresponse)espresponse->println ("Error: WiFi is not enabled!"); + return false; +#endif + } + else if (wifiMode == ESP_BT) { +#if defined (ENABLE_BLUETOOTH) + bt_config.begin(); +#else + if(espresponse)espresponse->println ("Error: Bluetooth is not enabled!"); + return false; +#endif + } else { + if(espresponse)espresponse->println ("[MSG: Radio is Off]"); + return false; + } + } else if(espresponse)espresponse->println ("[MSG: Radio is Off]"); + } + } + } + break; +#endif +#ifdef ENABLE_WIFI + //Set HTTP state which can be ON, OFF + //[ESP120]pwd= + case 120: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + int8_t Mode = prefs.getChar(HTTP_ENABLE_ENTRY, DEFAULT_HTTP_STATE); + espresponse->println((Mode == 0)?"OFF":"ON"); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter.toUpperCase(); + if (!((parameter == "ON") || (parameter == "OFF"))) { + if(espresponse)espresponse->println ("Error: only ON or OFF mode supported!"); + response = false; + return false; + } else { + Preferences prefs; + prefs.begin(NAMESPACE, false); + int8_t bbuf = (parameter == "ON")?1:0; + if (prefs.putChar(HTTP_ENABLE_ENTRY, bbuf) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + } + break; + //Set HTTP port + //[ESP121]pwd= + case 121: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + int port = prefs.getUShort(HTTP_PORT_ENTRY, DEFAULT_WEBSERVER_PORT); + espresponse->println(String(port).c_str()); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + int ibuf = parameter.toInt(); + if ((ibuf > MAX_HTTP_PORT) || (ibuf < MIN_HTTP_PORT)) { + if(espresponse)espresponse->println ("Error: Incorrect port!"); + response = false; + return false; + } + Preferences prefs; + prefs.begin(NAMESPACE, false); + + if (prefs.putUShort(HTTP_PORT_ENTRY, ibuf) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + + } + break; + //Set Telnet state which can be ON, OFF + //[ESP130]pwd= + case 130: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + int8_t Mode = prefs.getChar(TELNET_ENABLE_ENTRY, DEFAULT_TELNET_STATE); + espresponse->println((Mode == 0)?"OFF":"ON"); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter.toUpperCase(); + if (!((parameter == "ON") || (parameter == "OFF"))) { + if(espresponse)espresponse->println ("Error: only ON or OFF mode supported!"); + response = false; + return false; + } else { + Preferences prefs; + prefs.begin(NAMESPACE, false); + int8_t bbuf = (parameter == "ON")?1:0; + if (prefs.putChar(TELNET_ENABLE_ENTRY, bbuf) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + } + break; + //Set Telnet port + //[ESP131]pwd= + case 131: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + int port = prefs.getUShort(TELNET_PORT_ENTRY, DEFAULT_TELNETSERVER_PORT); + espresponse->println(String(port).c_str()); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + int ibuf = parameter.toInt(); + if ((ibuf > MAX_TELNET_PORT) || (ibuf < MIN_TELNET_PORT)) { + if(espresponse)espresponse->println ("Error: Incorrect port!"); + response = false; + return false; + } + Preferences prefs; + prefs.begin(NAMESPACE, false); + + if (prefs.putUShort(TELNET_PORT_ENTRY, ibuf) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + + } + break; +#endif + +#ifdef ENABLE_BLUETOOTH + //Get/Set btname + //[ESP140]< Bluetooth name> pwd= + case 140: { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; +#ifdef ENABLE_AUTHENTICATION + if ((auth_type != LEVEL_ADMIN) && (parameter.length() > 0)) { + espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + //Get btname + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + String defV = DEFAULT_BT_NAME; + espresponse->println(prefs.getString(BT_NAME_ENTRY, defV).c_str()); + prefs.end(); + } else { //set BT name + if (!bt_config.isBTnameValid (parameter.c_str() ) ) { + if(espresponse)espresponse->println ("Error: Incorrect name!"); + response = false; + } else { + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(BT_NAME_ENTRY, parameter) == 0){ + response = false; + if(espresponse)espresponse->println ("Error: Set failed!"); + } else if(espresponse)espresponse->println ("ok"); + prefs.end(); + } + } + } + break; +#endif + //Get SD Card Status + //[ESP200] + case 200: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + espresponse->println ("Error: Wrong authentication!"); + return false; + + } +#endif + if (!espresponse) return false; + String resp = "No SD card"; +#ifdef ENABLE_SD_CARD + + int8_t state = get_sd_state(true); + if (state == SDCARD_IDLE)resp="SD card detected"; + else if (state == SDCARD_NOT_PRESENT)resp="No SD card"; + else resp="Busy"; +#endif + espresponse->println (resp.c_str()); + } + break; +#ifdef ENABLE_SD_CARD + //Get SD Card Content + //[ESP210] + case 210: + { + if (!espresponse) return false; +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + espresponse->println ("Error: Wrong authentication!"); + return false; + + } +#endif + int8_t state = get_sd_state(true); + if (state == SDCARD_IDLE) { + listDir(SD, "/", 10, espresponse->client()); + String ssd = "[SD Free:" + ESPResponseStream::formatBytes(SD.totalBytes() - SD.usedBytes()); + ssd +=" Used:" + ESPResponseStream::formatBytes(SD.usedBytes()); + ssd +=" Total:" + ESPResponseStream::formatBytes(SD.totalBytes()); + ssd +="]"; + espresponse->println (""); + espresponse->println (ssd.c_str()); + } + else espresponse->println ((state == SDCARD_NOT_PRESENT) ? "No SD card" : "Busy"); + } + break; + + //Delete SD Card file / directory + //[ESP215]pwd= + case 215: + { + if (!espresponse) return false; +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + espresponse->println ("Error: Wrong authentication!"); + return false; + + } +#endif + parameter = get_param (cmd_params, "", true); + if (parameter.length() != 0) { + int8_t state = get_sd_state(true); + parameter.trim(); + if (parameter[0] != '/'){ + parameter = "/" + parameter; + } + if (state == SDCARD_IDLE) { + File file2del = SD.open(parameter.c_str()); + if (file2del) { + if (file2del.isDirectory()) { + if (!SD.rmdir((char *)parameter.c_str())) { + espresponse->println ("Error: Cannot delete directory! Is directory empty?"); + } else { + espresponse->println ("Directory deleted."); + } + } else { + if (!SD.remove((char *)parameter.c_str())) { + espresponse->println ("Error: Cannot delete file!"); + } else { + espresponse->println ("File deleted."); + } + } + } else { + espresponse->println ("Error: Cannot stat file!"); + } + file2del.close(); + } else { + espresponse->println ((state == SDCARD_NOT_PRESENT) ? "No SD card" : "Busy"); + } + } else { + espresponse->println ("Error: Missing file name!"); + } + } + break; + //print SD file + //[ESP220] + case 220: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if(espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if (parameter.length() == 0){ + if(espresponse)espresponse->println ("Error: Missing file name!"); + return false; + } + int8_t state = get_sd_state(true); + if (state != SDCARD_IDLE) { + espresponse->println ((state == SDCARD_NOT_PRESENT) ? "No SD card" : "Busy"); + return false; + } + if (sys.state != STATE_IDLE) { + if(espresponse)espresponse->println ("Busy"); + return false; + } + + if (!openFile(SD, parameter.c_str())){ + report_status_message(STATUS_SD_FAILED_READ, (espresponse)?espresponse->client(): CLIENT_ALL); + espresponse->println (""); + return false; + } + char fileLine[255]; + SD_client = (espresponse)?espresponse->client(): CLIENT_ALL; + if (!readFileLine(fileLine)) { + //No need notification here it is just a macro + closeFile(); + espresponse->println (""); + return false; + } else { + report_status_message(gc_execute_line(fileLine, (espresponse)?espresponse->client(): CLIENT_ALL), (espresponse)?espresponse->client(): CLIENT_ALL); // execute the first line + } + report_realtime_status( (espresponse)?espresponse->client(): CLIENT_ALL); + espresponse->println (""); + } + break; +#endif + //Get full ESP32 settings content + //[ESP400] + case 400: + { + String v; + String defV; + Preferences prefs; + if (!espresponse) return false; +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + + espresponse->print("{\"EEPROM\":["); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + prefs.begin(NAMESPACE, true); +#ifdef ENABLE_WIFI + int8_t vi; + //1 - Hostname + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (HOSTNAME_ENTRY); + espresponse->print ("\",\"T\":\"S\",\"V\":\""); + espresponse->print (wifi_config.Hostname().c_str()); + espresponse->print ("\",\"H\":\"Hostname\" ,\"S\":\""); + espresponse->print (String(MAX_HOSTNAME_LENGTH).c_str()); + espresponse->print ("\", \"M\":\""); + espresponse->print (String(MIN_HOSTNAME_LENGTH).c_str()); + espresponse->print ("\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); +#ifdef ENABLE_HTTP + //2 - http protocol mode + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (HTTP_ENABLE_ENTRY); + espresponse->print ("\",\"T\":\"B\",\"V\":\""); + vi = prefs.getChar(HTTP_ENABLE_ENTRY, 1); + espresponse->print (String(vi).c_str()); + espresponse->print ("\",\"H\":\"HTTP protocol\",\"O\":[{\"Enabled\":\"1\"},{\"Disabled\":\"0\"}]}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + //3 - http port + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (HTTP_PORT_ENTRY); + espresponse->print ("\",\"T\":\"I\",\"V\":\""); + espresponse->print (String(web_server.port()).c_str()); + espresponse->print ("\",\"H\":\"HTTP Port\",\"S\":\""); + espresponse->print (String(MAX_HTTP_PORT).c_str()); + espresponse->print ("\",\"M\":\""); + espresponse->print (String(MIN_HTTP_PORT).c_str()); + espresponse->print ("\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); +#endif + +#ifdef ENABLE_TELNET + //4 - telnet protocol mode + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (TELNET_ENABLE_ENTRY); + espresponse->print ("\",\"T\":\"B\",\"V\":\""); + vi = prefs.getChar(TELNET_ENABLE_ENTRY, DEFAULT_TELNET_STATE); + espresponse->print (String(vi).c_str()); + espresponse->print ("\",\"H\":\"Telnet protocol\",\"O\":[{\"Enabled\":\"1\"},{\"Disabled\":\"0\"}]}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + //5 - telnet Port + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (TELNET_PORT_ENTRY); + espresponse->print ("\",\"T\":\"I\",\"V\":\""); + espresponse->print (String(telnet_server.port()).c_str()); + espresponse->print ("\",\"H\":\"Telnet Port\",\"S\":\""); + espresponse->print (String(MAX_TELNET_PORT).c_str()); + espresponse->print ("\",\"M\":\""); + espresponse->print (String(MIN_TELNET_PORT).c_str()); + espresponse->print ("\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); +#endif + //6 - radio mode + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (ESP_RADIO_MODE); + espresponse->print ("\",\"T\":\"B\",\"V\":\""); + vi = prefs.getChar(ESP_RADIO_MODE, ESP_RADIO_OFF); + espresponse->print (String(vi).c_str()); + espresponse->print ("\",\"H\":\"Radio mode\",\"O\":[{\"None\":\"0\"}"); +#ifdef ENABLE_WIFI + espresponse->print (",{\"STA\":\"1\"},{\"AP\":\"2\"}"); +#endif +#ifdef ENABLE_BLUETOOTH + espresponse->print (",{\"BT\":\"3\"}"); +#endif + espresponse->print ("]}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //7 - STA SSID + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (STA_SSID_ENTRY); + espresponse->print ("\",\"T\":\"S\",\"V\":\""); + defV = DEFAULT_STA_SSID; + espresponse->print (prefs.getString(STA_SSID_ENTRY, defV).c_str()); + espresponse->print ("\",\"S\":\""); + espresponse->print (String(MAX_SSID_LENGTH).c_str()); + espresponse->print ("\",\"H\":\"Station SSID\",\"M\":\""); + espresponse->print (String(MIN_SSID_LENGTH).c_str()); + espresponse->print ("\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //8 - STA password + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (STA_PWD_ENTRY); + espresponse->print ("\",\"T\":\"S\",\"V\":\""); + espresponse->print (HIDDEN_PASSWORD); + espresponse->print ("\",\"S\":\""); + espresponse->print (String(MAX_PASSWORD_LENGTH).c_str()); + espresponse->print ("\",\"H\":\"Station Password\",\"M\":\""); + espresponse->print (String(MIN_PASSWORD_LENGTH).c_str()); + espresponse->print ("\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + // 9 - STA IP mode + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (STA_IP_MODE_ENTRY); + espresponse->print ("\",\"T\":\"B\",\"V\":\""); + espresponse->print (String(prefs.getChar(STA_IP_MODE_ENTRY, DHCP_MODE)).c_str()); + espresponse->print ("\",\"H\":\"Station IP Mode\",\"O\":[{\"DHCP\":\"0\"},{\"Static\":\"1\"}]}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //10-STA static IP + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (STA_IP_ENTRY); + espresponse->print ("\",\"T\":\"A\",\"V\":\""); + espresponse->print (wifi_config.IP_string_from_int(prefs.getInt(STA_IP_ENTRY, 0)).c_str()); + espresponse->print ("\",\"H\":\"Station Static IP\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //11-STA static Gateway + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (STA_GW_ENTRY); + espresponse->print ("\",\"T\":\"A\",\"V\":\""); + espresponse->print (wifi_config.IP_string_from_int(prefs.getInt(STA_GW_ENTRY, 0)).c_str()); + espresponse->print ("\",\"H\":\"Station Static Gateway\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //12-STA static Mask + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (STA_MK_ENTRY); + espresponse->print ("\",\"T\":\"A\",\"V\":\""); + espresponse->print (wifi_config.IP_string_from_int(prefs.getInt(STA_MK_ENTRY, 0)).c_str()); + espresponse->print ("\",\"H\":\"Station Static Mask\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //13 - AP SSID + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (AP_SSID_ENTRY); + espresponse->print ("\",\"T\":\"S\",\"V\":\""); + defV = DEFAULT_AP_SSID; + espresponse->print (prefs.getString(AP_SSID_ENTRY, defV).c_str()); + espresponse->print ("\",\"S\":\""); + espresponse->print (String(MAX_SSID_LENGTH).c_str()); + espresponse->print ("\",\"H\":\"AP SSID\",\"M\":\""); + espresponse->print (String(MIN_SSID_LENGTH).c_str()); + espresponse->print ("\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //14 - AP password + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (AP_PWD_ENTRY); + espresponse->print ("\",\"T\":\"S\",\"V\":\""); + espresponse->print (HIDDEN_PASSWORD); + espresponse->print ("\",\"S\":\""); + espresponse->print (String(MAX_PASSWORD_LENGTH).c_str()); + espresponse->print ("\",\"H\":\"AP Password\",\"M\":\""); + espresponse->print (String(MIN_PASSWORD_LENGTH).c_str()); + espresponse->print ("\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //15 - AP static IP + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (AP_IP_ENTRY); + espresponse->print ("\",\"T\":\"A\",\"V\":\""); + defV = DEFAULT_AP_IP; + espresponse->print (wifi_config.IP_string_from_int(prefs.getInt(AP_IP_ENTRY, wifi_config.IP_int_from_string(defV))).c_str()); + espresponse->print ("\",\"H\":\"AP Static IP\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //16 - AP Channel + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (AP_CHANNEL_ENTRY); + espresponse->print ("\",\"T\":\"B\",\"V\":\""); + espresponse->print (String(prefs.getChar(AP_CHANNEL_ENTRY, DEFAULT_AP_CHANNEL)).c_str()); + espresponse->print ("\",\"H\":\"AP Channel\",\"O\":["); + for (int i = MIN_CHANNEL; i <= MAX_CHANNEL ; i++) { + espresponse->print ("{\""); + espresponse->print (String(i).c_str()); + espresponse->print ("\":\""); + espresponse->print (String(i).c_str()); + espresponse->print ("\"}"); + if (i < MAX_CHANNEL) { + espresponse->print (","); + } + } + espresponse->print ("]}"); +#ifdef ENABLE_NOTIFICATIONS + espresponse->print (","); + //Notification type + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (NOTIFICATION_TYPE); + espresponse->print ("\",\"T\":\"B\",\"V\":\""); + vi = prefs.getChar(NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_TYPE); + espresponse->print (String(vi).c_str()); + espresponse->print ("\",\"H\":\"Notification type\",\"O\":[{\"None\":\"0\"}"); + espresponse->print (",{\"Line\":\"3\"},{\"Pushover\":\"1\"}"); + espresponse->print (",{\"Email\":\"2\"}"); + espresponse->print ("]}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //Notification token 1 + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (NOTIFICATION_T1); + espresponse->print ("\",\"T\":\"S\",\"V\":\""); + defV = DEFAULT_TOKEN; + espresponse->print (prefs.getString(NOTIFICATION_T1, defV).c_str()); + espresponse->print ("\",\"S\":\""); + espresponse->print (String(MAX_NOTIFICATION_TOKEN_LENGTH).c_str()); + espresponse->print ("\",\"H\":\"Notification Token 1\",\"M\":\""); + espresponse->print (String(MIN_NOTIFICATION_TOKEN_LENGTH).c_str()); + espresponse->print ("\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //Notification token 2 + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (NOTIFICATION_T2); + espresponse->print ("\",\"T\":\"S\",\"V\":\""); + defV = DEFAULT_TOKEN; + espresponse->print (prefs.getString(NOTIFICATION_T2, defV).c_str()); + espresponse->print ("\",\"S\":\""); + espresponse->print (String(MAX_NOTIFICATION_TOKEN_LENGTH).c_str()); + espresponse->print ("\",\"H\":\"Notification Token 2\",\"M\":\""); + espresponse->print (String(MIN_NOTIFICATION_TOKEN_LENGTH).c_str()); + espresponse->print ("\"}"); + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + + //Notification settings + espresponse->print ("{\"F\":\"network\",\"P\":\""); + espresponse->print (NOTIFICATION_TS); + espresponse->print ("\",\"T\":\"S\",\"V\":\""); + defV = DEFAULT_TOKEN; + espresponse->print (prefs.getString(NOTIFICATION_TS, defV).c_str()); + espresponse->print ("\",\"S\":\""); + espresponse->print (String(MAX_NOTIFICATION_SETTING_LENGTH).c_str()); + espresponse->print ("\",\"H\":\"Notification Settings\",\"M\":\""); + espresponse->print (String(MIN_NOTIFICATION_TOKEN_LENGTH).c_str()); + espresponse->print ("\"}"); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); +#endif //ENABLE_NOTIFICATIONS +#endif + espresponse->print ("]}"); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + prefs.end(); + } + break; + + //Set EEPROM setting + //[ESP401]P= T= V= pwd= + case 401: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + //check validity of parameters + String spos = get_param (cmd_params, "P=", false); + String styp = get_param (cmd_params, "T=", false); + String sval = get_param (cmd_params, "V=", true); + spos.trim(); + sval.trim(); + if (spos.length() == 0) { + response = false; + } + if (! (styp == "B" || styp == "S" || styp == "A" || styp == "I" || styp == "F") ) { + response = false; + } + if ((sval.length() == 0) +#if defined (ENABLE_WIFI) + && !((spos==AP_PWD_ENTRY) || (spos==STA_PWD_ENTRY)) +#endif + ){ + response = false; + } + + if (response) { + Preferences prefs; + prefs.begin(NAMESPACE, false); + //Byte value + if ((styp == "B") || (styp == "F")){ + int8_t bbuf = sval.toInt(); + if (prefs.putChar(spos.c_str(), bbuf) ==0 ) { + response = false; + } else { +#if defined (ENABLE_WIFI) + //dynamique refresh is better than restart the board + if (spos == ESP_RADIO_MODE){ + //TODO + } + if (spos == AP_CHANNEL_ENTRY) { + //TODO + } +#if defined (ENABLE_HTTP) + if (spos == HTTP_ENABLE_ENTRY) { + //TODO + } +#endif +#if defined (ENABLE_TELNET) + if (spos == TELNET_ENABLE_ENTRY) { + //TODO + } +#endif +#endif + } + } + //Integer value + if (styp == "I") { + int16_t ibuf = sval.toInt(); + if (prefs.putUShort(spos.c_str(), ibuf) == 0) { + response = false; + } else { +#if defined (ENABLE_WIFI) +#if defined (ENABLE_HTTP) + if (spos == HTTP_PORT_ENTRY){ + //TODO + } +#endif +#if defined (ENABLE_TELNET) + if (spos == TELNET_PORT_ENTRY){ + //TODO + //Serial.println(ibuf); + } +#endif +#endif + } + + } + //String value + if (styp == "S") { + if (prefs.putString(spos.c_str(), sval) != sval.length()) { + response = false; + } else { +#if defined (ENABLE_WIFI) + if (spos == HOSTNAME_ENTRY){ + //TODO + } + if (spos == STA_SSID_ENTRY){ + //TODO + } + if (spos == STA_PWD_ENTRY){ + //TODO + } + if (spos == AP_SSID_ENTRY){ + //TODO + } + if (spos == AP_PWD_ENTRY){ + //TODO + } +#endif + } + + } +#if defined (ENABLE_WIFI) + //IP address + if (styp == "A") { + if (prefs.putInt(spos.c_str(), wifi_config.IP_int_from_string(sval)) == 0) { + response = false; + } else { + + if (spos == STA_IP_ENTRY){ + //TODO + } + if (spos == STA_GW_ENTRY){ + //TODO + } + if (spos == STA_MK_ENTRY){ + //TODO + } + if (spos == AP_IP_ENTRY){ + //TODO + } + } + } +#endif + prefs.end(); + } + if (!response) { + if (espresponse) espresponse->println ("Error: Incorrect Command"); + } else { + if (espresponse) espresponse->println ("ok"); + } + + } + break; +#if defined (ENABLE_WIFI) + //Get available AP list (limited to 30) + //output is JSON + //[ESP410] + case 410: { + if (!espresponse)return false; +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + espresponse->print("{\"AP_LIST\":["); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + int n = WiFi.scanComplete(); + if (n == -2) { + WiFi.scanNetworks (true); + } else if (n) { + for (int i = 0; i < n; ++i) { + if (i > 0) { + espresponse->print (","); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + } + espresponse->print ("{\"SSID\":\""); + espresponse->print (WiFi.SSID (i).c_str()); + espresponse->print ("\",\"SIGNAL\":\""); + espresponse->print (String(wifi_config.getSignal (WiFi.RSSI (i) )).c_str()); + espresponse->print ("\",\"IS_PROTECTED\":\""); + + if (WiFi.encryptionType (i) == WIFI_AUTH_OPEN) { + espresponse->print ("0"); + } else { + espresponse->print ("1"); + } + espresponse->print ("\"}"); + } + } + WiFi.scanDelete(); + if (WiFi.scanComplete() == -2) { + WiFi.scanNetworks (true); + } + espresponse->print ("]}"); + if(espresponse->client() != CLIENT_WEBUI)espresponse->println(""); + } + break; +#endif + //Get ESP current status + case 420: + { +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + if (!espresponse)return false; + espresponse->print ("Chip ID: "); + espresponse->print (String ( (uint16_t) (ESP.getEfuseMac() >> 32) ).c_str()); + espresponse->println(""); + espresponse->print ("CPU Frequency: "); + espresponse->print (String (ESP.getCpuFreqMHz() ).c_str()); + espresponse->print ("Mhz"); + espresponse->println(""); + espresponse->print ("CPU Temperature: "); + espresponse->print (String (temperatureRead(), 1).c_str()); + if(espresponse->client() == CLIENT_WEBUI)espresponse->print ("°"); + espresponse->print ("C"); + espresponse->println(""); + espresponse->print ("Free memory: "); + espresponse->print (ESPResponseStream::formatBytes (ESP.getFreeHeap()).c_str()); + espresponse->println(""); + espresponse->print ("SDK: "); + espresponse->print (ESP.getSdkVersion()); + espresponse->println(""); + espresponse->print ("Flash Size: "); + espresponse->print (ESPResponseStream::formatBytes (ESP.getFlashChipSize()).c_str()); + espresponse->println(""); +#if defined (ENABLE_WIFI) + if (WiFi.getMode() != WIFI_MODE_NULL){ + espresponse->print ("Available Size for update: "); + //Is OTA available ? + size_t flashsize = 0; + if (esp_ota_get_running_partition()) { + const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL); + if (partition) { + flashsize = partition->size; + } + } + espresponse->print (ESPResponseStream::formatBytes (flashsize).c_str()); + + espresponse->println(""); + } + if (WiFi.getMode() != WIFI_MODE_NULL){ + espresponse->print ("Available Size for SPIFFS: "); + espresponse->print (ESPResponseStream::formatBytes (SPIFFS.totalBytes()).c_str()); + espresponse->println(""); + } +#endif + espresponse->print ("Baud rate: "); + long br = Serial.baudRate(); + //workaround for ESP32 + if (br == 115201) { + br = 115200; + } + if (br == 230423) { + br = 230400; + } + espresponse->print (String(br).c_str()); + espresponse->println(""); + espresponse->print ("Sleep mode: "); + if (WiFi.getSleep())espresponse->print ("Modem"); + else espresponse->print ("None"); + espresponse->println(""); +#if defined (ENABLE_WIFI) +#if defined (ENABLE_HTTP) + if (WiFi.getMode() != WIFI_MODE_NULL){ + espresponse->print ("Web port: "); + espresponse->print (String(web_server.port()).c_str()); + espresponse->println(""); + } +#endif +#if defined (ENABLE_TELNET) + if (WiFi.getMode() != WIFI_MODE_NULL){ + espresponse->print ("Data port: "); + espresponse->print (String(telnet_server.port()).c_str()); + espresponse->println(""); + } +#endif + if (WiFi.getMode() != WIFI_MODE_NULL){ + espresponse->print ("Hostname: "); + espresponse->print ( wifi_config.Hostname().c_str()); + espresponse->println(""); + } + espresponse->print ("Current WiFi Mode: "); + if (WiFi.getMode() == WIFI_STA) { + espresponse->print ("STA ("); + espresponse->print ( WiFi.macAddress().c_str()); + espresponse->print (")"); + espresponse->println(""); + espresponse->print ("Connected to: "); + if (WiFi.isConnected()){ //in theory no need but ... + espresponse->print (WiFi.SSID().c_str()); + espresponse->println(""); + espresponse->print ("Signal: "); + espresponse->print ( String(wifi_config.getSignal (WiFi.RSSI())).c_str()); + espresponse->print ("%"); + espresponse->println(""); + uint8_t PhyMode; + esp_wifi_get_protocol (ESP_IF_WIFI_STA, &PhyMode); + espresponse->print ("Phy Mode: "); + if (PhyMode == (WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N)) espresponse->print ("11n"); + else if (PhyMode == (WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G)) espresponse->print ("11g"); + else if (PhyMode == (WIFI_PROTOCOL_11B )) espresponse->print ("11b"); + else espresponse->print ("???"); + espresponse->println(""); + espresponse->print ("Channel: "); + espresponse->print (String (WiFi.channel()).c_str()); + espresponse->println(""); + espresponse->print ("IP Mode: "); + tcpip_adapter_dhcp_status_t dhcp_status; + tcpip_adapter_dhcpc_get_status (TCPIP_ADAPTER_IF_STA, &dhcp_status); + if (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED)espresponse->print ("DHCP"); + else espresponse->print ("Static"); + espresponse->println(""); + espresponse->print ("IP: "); + espresponse->print (WiFi.localIP().toString().c_str()); + espresponse->println(""); + espresponse->print ("Gateway: "); + espresponse->print (WiFi.gatewayIP().toString().c_str()); + espresponse->println(""); + espresponse->print ("Mask: "); + espresponse->print (WiFi.subnetMask().toString().c_str()); + espresponse->println(""); + espresponse->print ("DNS: "); + espresponse->print (WiFi.dnsIP().toString().c_str()); + espresponse->println(""); + } //this is web command so connection => no command + espresponse->print ("Disabled Mode: "); + espresponse->print ("AP ("); + espresponse->print (WiFi.softAPmacAddress().c_str()); + espresponse->print (")"); + espresponse->println(""); + } else if (WiFi.getMode() == WIFI_AP) { + espresponse->print ("AP ("); + espresponse->print (WiFi.softAPmacAddress().c_str()); + espresponse->print (")"); + espresponse->println(""); + wifi_config_t conf; + esp_wifi_get_config (ESP_IF_WIFI_AP, &conf); + espresponse->print ("SSID: "); + espresponse->print ((const char*) conf.ap.ssid); + espresponse->println(""); + espresponse->print ("Visible: "); + espresponse->print ( (conf.ap.ssid_hidden == 0) ? "Yes" : "No"); + espresponse->println(""); + espresponse->print ("Authentication: "); + if (conf.ap.authmode == WIFI_AUTH_OPEN) { + espresponse->print ("None"); + } else if (conf.ap.authmode == WIFI_AUTH_WEP) { + espresponse->print ("WEP"); + } else if (conf.ap.authmode == WIFI_AUTH_WPA_PSK) { + espresponse->print ("WPA"); + } else if (conf.ap.authmode == WIFI_AUTH_WPA2_PSK) { + espresponse->print ("WPA2"); + } else { + espresponse->print ("WPA/WPA2"); + } + espresponse->println(""); + espresponse->print ("Max Connections: "); + espresponse->print (String(conf.ap.max_connection).c_str()); + espresponse->println(""); + espresponse->print ("DHCP Server: "); + tcpip_adapter_dhcp_status_t dhcp_status; + tcpip_adapter_dhcps_get_status (TCPIP_ADAPTER_IF_AP, &dhcp_status); + if (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED)espresponse->print ("Started"); + else espresponse->print ("Stopped"); + espresponse->println(""); + espresponse->print ("IP: "); + espresponse->print (WiFi.softAPIP().toString().c_str()); + espresponse->println(""); + tcpip_adapter_ip_info_t ip_AP; + tcpip_adapter_get_ip_info (TCPIP_ADAPTER_IF_AP, &ip_AP); + espresponse->print ("Gateway: "); + espresponse->print (IPAddress (ip_AP.gw.addr).toString().c_str()); + espresponse->println(""); + espresponse->print ("Mask: "); + espresponse->print (IPAddress (ip_AP.netmask.addr).toString().c_str()); + espresponse->println(""); + espresponse->print ("Connected clients: "); + wifi_sta_list_t station; + tcpip_adapter_sta_list_t tcpip_sta_list; + esp_wifi_ap_get_sta_list (&station); + tcpip_adapter_get_sta_list (&station, &tcpip_sta_list); + espresponse->print (String(station.num).c_str()); + espresponse->println(""); + for (int i = 0; i < station.num; i++) { + espresponse->print (wifi_config.mac2str(tcpip_sta_list.sta[i].mac)); + espresponse->print (" "); + espresponse->print ( IPAddress (tcpip_sta_list.sta[i].ip.addr).toString().c_str()); + espresponse->println(""); + } + espresponse->print ("Disabled Mode: "); + espresponse->print ("STA ("); + espresponse->print (WiFi.macAddress().c_str()); + espresponse->print (")"); + espresponse->println(""); + } else if (WiFi.getMode() == WIFI_AP_STA) //we should not be in this state but just in case .... + { + espresponse->print ("Mixed"); + espresponse->println(""); + espresponse->print ("STA ("); + espresponse->print (WiFi.macAddress().c_str()); + espresponse->print (")"); + espresponse->println(""); + espresponse->print ("AP ("); + espresponse->print (WiFi.softAPmacAddress().c_str()); + espresponse->print (")"); + espresponse->println(""); + + } else { //we should not be there if no wifi .... + espresponse->print ("Off"); + espresponse->println(""); + } +#endif +#ifdef ENABLE_BLUETOOTH + espresponse->print ("Current BT Mode: "); + if (bt_config.Is_BT_on()){ + espresponse->println("On"); + espresponse->print ("BT Name: "); + espresponse->print (bt_config.BTname().c_str()); + espresponse->print ("("); + espresponse->print (bt_config.device_address()); + espresponse->println(")"); + espresponse->print ("Status: "); + if(SerialBT.hasClient()) { + espresponse->print ("Connected with "); + espresponse->print (bt_config._btclient.c_str()); + } + else espresponse->print ("Not connected"); + } else{ + espresponse->print ("Off"); + } + espresponse->println(""); +#endif +#ifdef ENABLE_NOTIFICATIONS + espresponse->print ("Notifications: "); + espresponse->print (notificationsservice.started()?"Enabled":"Disabled"); + if (notificationsservice.started()) { + espresponse->print ("("); + espresponse->print (notificationsservice.getTypeString()); + espresponse->print (")"); + } + espresponse->println(""); +#endif + //TODO to complete + espresponse->print ("FW version: "); + espresponse->print (GRBL_VERSION); + espresponse->print (" ("); + espresponse->print (GRBL_VERSION_BUILD); + espresponse->print (") (ESP32)"); + espresponse->println(""); + } + break; + //Set ESP mode + //cmd is RESTART + //[ESP444] + case 444: + parameter = get_param(cmd_params,"", true); +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + { + if (parameter=="RESTART") { + grbl_send(CLIENT_ALL,"[MSG:Restart ongoing]\r\n"); + COMMANDS::restart_ESP(); + } else response = false; + } + if (!response) { + if (espresponse)espresponse->println ("Error: Incorrect Command"); + } else { + if (espresponse)espresponse->println ("ok"); + } + break; +#ifdef ENABLE_AUTHENTICATION + //Change / Reset user password + //[ESP555] + case 555: { + if (auth_type == LEVEL_ADMIN) { + parameter = get_param (cmd_params, "", true); + if (parameter.length() == 0) { + Preferences prefs; + parameter = DEFAULT_USER_PWD; + prefs.begin(NAMESPACE, false); + if (prefs.putString(USER_PWD_ENTRY, parameter) != parameter.length()){ + response = false; + if (espresponse)espresponse->println ("error"); + } else if (espresponse)espresponse->println ("ok"); + prefs.end(); + + } else { + if (isLocalPasswordValid (parameter.c_str() ) ) { + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(USER_PWD_ENTRY, parameter) != parameter.length()) { + response = false; + if (espresponse)espresponse->println ("error"); + } else if (espresponse)espresponse->println ("ok"); + prefs.end(); + } else { + if (espresponse)espresponse->println ("error"); + response = false; + } + } + } else { + if (espresponse)espresponse->println ("error"); + response = false; + } + break; + } +#endif +#ifdef ENABLE_NOTIFICATIONS + case 600: { //Send message + +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if (parameter.length() == 0) { + if (espresponse)espresponse->println ("Invalid message!"); + return false; + } + if (notificationsservice.sendMSG("GRBL Notification", parameter.c_str())) { + if (espresponse)espresponse->println ("ok"); + } else { + if (espresponse)espresponse->println ("Cannot send message!"); + return false; + } + } + break; + //Set/Get Notification settings + //[ESP610]type= T1= T2= TS= [pwd=] + //Get will give type and settings only not the protected T1/T2 + case 610: { //Send message + +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ((parameter.length() == 0) && !espresponse) return false; + //get + if (parameter.length() == 0) { + Preferences prefs; + prefs.begin(NAMESPACE, true); + uint8_t vi = prefs.getChar(NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_TYPE); + String defV = DEFAULT_TOKEN; + parameter = (vi == ESP_PUSHOVER_NOTIFICATION)?"PUSHOVER":(vi == ESP_LINE_NOTIFICATION)?"LINE":(vi == ESP_EMAIL_NOTIFICATION)?"EMAIL":"NONE"; + parameter+=" "; + parameter+=prefs.getString(NOTIFICATION_TS, defV); + espresponse->println(parameter.c_str()); + prefs.end(); + } else { //set +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + response = false; + parameter = get_param (cmd_params, "type=", false); + if (parameter.length() !=0) { + uint8_t bbuf = (parameter == "NONE")?0:(parameter == "PUSHOVER")?ESP_PUSHOVER_NOTIFICATION:(parameter == "LINE")?ESP_LINE_NOTIFICATION:(parameter == "EMAIL")?ESP_EMAIL_NOTIFICATION:255; + if (bbuf != 255){ + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putChar(NOTIFICATION_TYPE, bbuf) == 0){ + if(espresponse)espresponse->println ("Error: Set failed!"); + response = false; + } else { + response = true; + } + prefs.end(); + } else{ + if(espresponse)espresponse->println ("Error: wrong type!"); + response = false; + } + } + + parameter = get_param (cmd_params, "T1=", false); + if (parameter.length() !=0) { + if (parameter.length() <=MAX_NOTIFICATION_TOKEN_LENGTH ){ + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(NOTIFICATION_T1, parameter) == 0){ + if(espresponse)espresponse->println ("Error: Set failed!"); + response = false; + } else { + response = true; + } + prefs.end(); + } else{ + if(espresponse)espresponse->println ("Error: token 1!"); + response = false; + } + } + parameter = get_param (cmd_params, "T2=", false); + if (parameter.length() !=0) { + if (parameter.length() <=MAX_NOTIFICATION_TOKEN_LENGTH ){ + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(NOTIFICATION_T2, parameter) == 0){ + if(espresponse)espresponse->println ("Error: Set failed!"); + response = false; + } else { + response = true; + } + prefs.end(); + } else{ + if(espresponse)espresponse->println ("Error: token 2!"); + response = false; + } + } + parameter = get_param (cmd_params, "TS=", false); + if (parameter.length() !=0) { + if (parameter.length() <=MAX_NOTIFICATION_SETTING_LENGTH ){ + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(NOTIFICATION_TS, parameter) == 0){ + if(espresponse)espresponse->println ("Error: Set failed!"); + response = false; + } else { + response = true; + } + prefs.end(); + } else{ + if(espresponse)espresponse->println ("Error: settings!"); + response = false; + } + } + } + //update settings + notificationsservice.begin(); + if(espresponse && response)espresponse->println ("ok"); + + } + break; +#endif //ENABLE_NOTIFICATIONS + //[ESP700] pwd= + case 700: { //read local file + +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + if ( (parameter.length() > 0) && (parameter[0] != '/') ) { + parameter = "/" + parameter; + } + if (!SPIFFS.exists(parameter)){ + if (espresponse)espresponse->println ("Error:No such file!"); + response = false; + } else { + File currentfile = SPIFFS.open (parameter, FILE_READ); + if (currentfile) {//if file open success + //until no line in file + while (currentfile.available()) { + String currentline = currentfile.readStringUntil('\n'); + currentline.replace("\n",""); + currentline.replace("\r",""); + if (currentline.length() > 0) { + int ESPpos = currentline.indexOf ("[ESP"); + if (ESPpos > -1) { + //is there the second part? + int ESPpos2 = currentline.indexOf ("]", ESPpos); + if (ESPpos2 > -1) { + //Split in command and parameters + String cmd_part1 = currentline.substring (ESPpos + 4, ESPpos2); + String cmd_part2 = ""; + //is there space for parameters? + if (ESPpos2 < currentline.length() ) { + cmd_part2 = currentline.substring (ESPpos2 + 1); + } + //if command is a valid number then execute command + if(cmd_part1.toInt()!=0) { + if (!execute_internal_command(cmd_part1.toInt(),cmd_part2, auth_type, espresponse)) response = false; + } + //if not is not a valid [ESPXXX] command ignore it + } + } else { + //preprocess line + String processedline = ""; + char c; + uint8_t line_flags = 0; + for (uint16_t index=0; index < currentline.length(); index++){ + c = currentline[index]; + if (c == '\r' || c == ' ' || c == '\n') { + // 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); } + } + 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 { // add characters to the line + if (!line_flags) { + c = toupper(c); // make upper case + processedline += c; + } + } + } + if (processedline.length() > 0)gc_execute_line((char *)processedline.c_str(), CLIENT_WEBUI); + wait (1); + } + wait (1); + } + } + currentfile.close(); + if (espresponse)espresponse->println ("ok"); + } else { + if (espresponse)espresponse->println ("error"); + response = false; + } + } + break; + } + //Format SPIFFS + //[ESP710]FORMAT pwd= + case 710: +#ifdef ENABLE_AUTHENTICATION + if (auth_type != LEVEL_ADMIN) { + if (espresponse)espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + { + if (parameter == "FORMAT") { + if (espresponse)espresponse->print ("Formating"); + SPIFFS.format(); + if (espresponse)espresponse->println ("...Done"); + } else { + if (espresponse)espresponse->println ("error"); + response = false; + } + } + break; + //SPIFFS total size and used size + //[ESP720]
+ case 720: + if (!espresponse)return false; +#ifdef ENABLE_AUTHENTICATION + if (auth_type == LEVEL_GUEST) { + espresponse->println ("Error: Wrong authentication!"); + return false; + } +#endif + parameter = get_param (cmd_params, "", true); + espresponse->print (parameter.c_str()); + espresponse->print ("SPIFFS Total:"); + espresponse->print (ESPResponseStream::formatBytes (SPIFFS.totalBytes() ).c_str()); + espresponse->print (" Used:"); + espresponse->println (ESPResponseStream::formatBytes (SPIFFS.usedBytes() ).c_str()); + break; + //get fw version / fw target / hostname / authentication + //[ESP800] + case 800: + { + if (!espresponse)return false; + String resp; + resp = "FW version:"; + resp += GRBL_VERSION ; + resp += " ("; + resp += GRBL_VERSION_BUILD; + resp += ")"; + resp += " # FW target:grbl-embedded # FW HW:"; + #ifdef ENABLE_SD_CARD + resp += "Direct SD"; + #else + resp += "No SD"; + #endif + resp += " # primary sd:/sd # secondary sd:none # authentication:"; + #ifdef ENABLE_AUTHENTICATION + resp += "yes"; + #else + resp += "no"; + #endif + #if defined (ENABLE_WIFI) + #if defined (ENABLE_HTTP) + resp += " # webcommunication: Sync: "; + resp += String(web_server.port() + 1); + resp += ":"; + if (WiFi.getMode() == WIFI_MODE_AP) { + resp += WiFi.softAPIP().toString(); + } else if (WiFi.getMode() == WIFI_MODE_STA){ + resp += WiFi.localIP().toString(); + } else if (WiFi.getMode() == WIFI_MODE_APSTA) { + resp += WiFi.softAPIP().toString(); + } else { + resp += "0.0.0.0"; + } + #endif + resp += " # hostname:"; + resp += wifi_config.Hostname(); + if (WiFi.getMode() == WIFI_AP)resp += "(AP mode)"; + #endif + //to save time in decoding `?` + resp += " # axis:"; + resp += String(N_AXIS); + if (espresponse)espresponse->println (resp.c_str()); + } + break; + default: + if (espresponse)espresponse->println ("Error: Incorrect Command"); + response = false; + break; + } + return response; +} + + +String COMMANDS::get_param(String & cmd_params, const char * id, bool withspace) +{ + static String parameter; + String sid = id; + int start; + int end = -1; + if (cmd_params.indexOf("pwd=") == 0)cmd_params = " " + cmd_params; + parameter = ""; + //if no id it means it is first part of cmd + if (strlen (id) == 0) { + start = 0; + } + //else find id position + else { + start = cmd_params.indexOf (id); + } + //if no id found and not first part leave + if (start == -1 ) { + return parameter; + } + //password and SSID can have space so handle it + //if no space expected use space as delimiter + if (!withspace) { + end = cmd_params.indexOf (" ", start); + } +#ifdef ENABLE_AUTHENTICATION + //if space expected only one parameter but additional password may be present + else if (sid != " pwd=") { + end = cmd_params.indexOf (" pwd=", start); + } +#endif + //if no end found - take all + if (end == -1) { + end = cmd_params.length(); + } + //extract parameter + parameter = cmd_params.substring (start + strlen (id), end); + //be sure no extra space + parameter.trim(); + return parameter; +} + +#ifdef ENABLE_AUTHENTICATION + +bool COMMANDS::isLocalPasswordValid (const char * password) +{ + char c; + //limited size + if ( (strlen (password) > MAX_LOCAL_PASSWORD_LENGTH) || (strlen (password) < MIN_LOCAL_PASSWORD_LENGTH) ) { + return false; + } + //no space allowed + for (int i = 0; i < strlen (password); i++) { + c = password[i]; + if (c == ' ') { + return false; + } + } + return true; +} + +//check admin password +bool COMMANDS::isadmin (String & cmd_params) +{ String adminpassword; + String sadminPassword; + Preferences prefs; + prefs.begin(NAMESPACE, true); + String defV = DEFAULT_ADMIN_PWD; + sadminPassword = prefs.getString(ADMIN_PWD_ENTRY, defV); + prefs.end(); + adminpassword = get_param (cmd_params, "pwd=", true); + if (!sadminPassword.equals (adminpassword) ) { + return false; + } else { + return true; + } +} +//check user password - admin password is also valid +bool COMMANDS::isuser (String & cmd_params) +{ + String userpassword; + String suserPassword; + Preferences prefs; + prefs.begin(NAMESPACE, true); + String defV = DEFAULT_USER_PWD; + suserPassword = prefs.getString(USER_PWD_ENTRY, defV); + prefs.end(); + userpassword = get_param (cmd_params, "pwd=", true); + //it is not user password + if (!suserPassword.equals (userpassword) ) { + //check admin password + return isadmin (cmd_params); + } else { + return true; + } +} +#endif + +//check is valid [ESPXXX] command +//return XXX as cmd and command as cmd_params +bool COMMANDS::check_command (const char * line, int * cmd, String & cmd_params) +{ + String buffer = line; + bool result = false; + int ESPpos = buffer.indexOf ("[ESP"); + if (ESPpos > -1) { + //is there the second part? + int ESPpos2 = buffer.indexOf ("]", ESPpos); + if (ESPpos2 > -1) { + //Split in command and parameters + String cmd_part1 = buffer.substring (ESPpos + 4, ESPpos2); + String cmd_part2 = ""; + //is there space for parameters? + if (ESPpos2 < buffer.length() ) { + cmd_part2 = buffer.substring (ESPpos2 + 1); + } + //if command is a valid number then execute command + if (cmd_part1.toInt() != 0) { + *cmd = cmd_part1.toInt(); + cmd_params = cmd_part2; + result = true; + } + //if not is not a valid [ESPXXX] command + } + } + return result; +} + +/** + * Restart ESP + */ +void COMMANDS::restart_ESP(){ + restart_ESP_module=true; +} + +/** + * Handle not critical actions that must be done in sync environement + */ +void COMMANDS::handle() { + COMMANDS::wait(0); + //in case of restart requested + if (restart_ESP_module) { + ESP.restart(); + while (1) {}; + } +} diff --git a/Grbl_Esp32-master/Grbl_Esp32/commands.h b/Grbl_Esp32-master/Grbl_Esp32/commands.h new file mode 100644 index 0000000..60a85e6 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/commands.h @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/config.h b/Grbl_Esp32-master/Grbl_Esp32/config.h new file mode 100644 index 0000000..57bee12 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/config.h @@ -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 . +*/ + +// 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 + +//#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< 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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/coolant_control.cpp b/Grbl_Esp32-master/Grbl_Esp32/coolant_control.cpp new file mode 100644 index 0000000..10e0833 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/coolant_control.cpp @@ -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 . +*/ + +#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); +} diff --git a/Grbl_Esp32-master/Grbl_Esp32/coolant_control.h b/Grbl_Esp32-master/Grbl_Esp32/coolant_control.h new file mode 100644 index 0000000..1511319 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/coolant_control.h @@ -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 . +*/ + +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/cpu_map.h b/Grbl_Esp32-master/Grbl_Esp32/cpu_map.h new file mode 100644 index 0000000..e558f09 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/cpu_map.h @@ -0,0 +1,1130 @@ +/* + cpu_map.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 . +*/ + +#ifndef cpu_map_h +//#define cpu_map_h + + /* + Not all pins can can work for all functions. + Check features like pull-ups, pwm, etc before + re-assigning numbers + + (gpio34-39) are inputs only and don't have software pullup/down functions + You MUST use external pull-ups or noise WILL cause problems. + + Unlike the AVR version certain pins are not forced into the same port. + Therefore, bit masks are not use the same way and typically should not be + changed. They are just preserved right now to make it easy to stay in sync + with AVR grbl + + */ + +//Set your pin definition +//let -1 to use default board pin +#define GRBL_SPI_SS -1 +#define GRBL_SPI_MOSI -1 +#define GRBL_SPI_MISO -1 +#define GRBL_SPI_SCK -1 +//Set your frequency +#define GRBL_SPI_FREQ 4000000 + +#ifdef CPU_MAP_TEST_DRIVE + /* + This is just a demo CPU_MAP for test driving. It creates a basic 3 axis machine, but + no actual i/o is used. It will appear that axes are moving, but they are virtual + + This can be uploaded to an unattached ESP32 or attached to unknown hardware. There is no risk + pins trying to output signals into a short, etc that could dmamge the ESP32 + + Assuming no changes have been made to the config.h file it is also a way to get he basic program + running so OTA (over the air) firmware loading can be done. + + */ + #define CPU_MAP_NAME "CPU_MAP_DEFAULT - Demo Only No I/O!" + + #define CONTROL_FEED_HOLD_PIN GPIO_NUM_21 // Uno A1 + + #define LIMIT_MASK 0 // no limit pins +#endif + +#ifdef CPU_MAP_ESP32 + // This is the CPU Map for the ESP32 Development Controller + // https://github.com/bdring/Grbl_ESP32_Development_Controller + // https://www.tindie.com/products/33366583/grbl_esp32-cnc-development-board-v35/ + + // Select the version (uncomment one of them) + #define CPU_MAP_V3p5 // version 3.5 and earlier + //#define CPU_MAP_V4 // version 4 or higher (in developement) + + #define USE_RMT_STEPS + + // It is OK to comment out any step and direction pins. This + // won't affect operation except that there will be no output + // form the pins. Grbl will virtually move the axis. This could + // be handy if you are using a servo, etc. for another axis. + #if (defined CPU_MAP_V4) + #define CPU_MAP_NAME "CPU_MAP_ESP32_V4" + #define X_DIRECTION_PIN GPIO_NUM_14 + #define Y_STEP_PIN GPIO_NUM_26 + #define Y_DIRECTION_PIN GPIO_NUM_15 + //#define COOLANT_FLOOD_PIN GPIO_NUM_25 + #define SPINDLE_PWM_PIN GPIO_NUM_2 + #define X_LIMIT_PIN GPIO_NUM_17 + #define Z_LIMIT_PIN GPIO_NUM_16 + #elif (defined CPU_MAP_V3p5) + #define CPU_MAP_NAME "CPU_MAP_ESP32_V3.5" + #define X_DIRECTION_PIN GPIO_NUM_26 + #define Y_STEP_PIN GPIO_NUM_14 + #define Y_DIRECTION_PIN GPIO_NUM_25 + //#define COOLANT_FLOOD_PIN GPIO_NUM_16 + #define SPINDLE_PWM_PIN GPIO_NUM_17 + #define X_LIMIT_PIN GPIO_NUM_13 // CRA4 + #define Z_LIMIT_PIN GPIO_NUM_15 + #endif + + #define X_STEP_PIN GPIO_NUM_12 + + #define X_RMT_CHANNEL 0 + + // #define Y_STEP_PIN (see versions above) + #define Y_RMT_CHANNEL 1 + + #define Z_STEP_PIN GPIO_NUM_27 + #define Z_DIRECTION_PIN GPIO_NUM_33 + #define Z_RMT_CHANNEL 2 + + // OK to comment out to use pin for other features + #define STEPPERS_DISABLE_PIN GPIO_NUM_2 // CRA4 + + //#define COOLANT_MIST_PIN GPIO_NUM_21 + #define USER_DIGITAL_PIN_1 GPIO_NUM_21 + #define USER_DIGITAL_PIN_2 GPIO_NUM_25 + + + #define SPINDLE_PWM_CHANNEL 0 + #define SPINDLE_PWM_BIT_PRECISION 8 + #define SPINDLE_ENABLE_PIN GPIO_NUM_22 + + // see versions for X and Z + #define Y_LIMIT_PIN GPIO_NUM_4 + #define LIMIT_MASK B111 + + #define PROBE_PIN GPIO_NUM_32 + + #define CONTROL_SAFETY_DOOR_PIN GPIO_NUM_35 // needs external pullup + #define CONTROL_RESET_PIN GPIO_NUM_34 // needs external pullup + #define CONTROL_FEED_HOLD_PIN GPIO_NUM_36 // needs external pullup + #define CONTROL_CYCLE_START_PIN GPIO_NUM_39 // needs external pullup +#endif + +#ifdef CPU_MAP_ESPDUINO_32 + // !!!! Experimental Untested !!!!! + // This is a CPU MAP for ESPDUINO-32 Boards and Protoneer V3 boards + // Note: Probe pin is mapped, but will require a 10k external pullup to 3.3V to work. + #define CPU_MAP_NAME "CPU_MAP_ESPDUINO_32" + + #define USE_RMT_STEPS + + #define X_STEP_PIN GPIO_NUM_26 // Uno D2 + #define X_DIRECTION_PIN GPIO_NUM_16 // Uno D5 + #define X_RMT_CHANNEL 0 + + #define Y_STEP_PIN GPIO_NUM_25 // Uno D3 + #define Y_DIRECTION_PIN GPIO_NUM_27 // Uno D6 + #define Y_RMT_CHANNEL 1 + + #define Z_STEP_PIN GPIO_NUM_17 // Uno D4 + #define Z_DIRECTION_PIN GPIO_NUM_14 // Uno D7 + #define Z_RMT_CHANNEL 2 + + // OK to comment out to use pin for other features + #define STEPPERS_DISABLE_PIN GPIO_NUM_12 // Uno D8 + + #define SPINDLE_PWM_PIN GPIO_NUM_19 // Uno D11 + #define SPINDLE_PWM_CHANNEL 0 + #define SPINDLE_PWM_BIT_PRECISION 8 // be sure to match this with SPINDLE_PWM_MAX_VALUE + #define SPINDLE_DIR_PIN GPIO_NUM_18 // Uno D13 + + #define COOLANT_FLOOD_PIN GPIO_NUM_34 // Uno A3 + #define COOLANT_MIST_PIN GPIO_NUM_36// Uno A4 + + #define X_LIMIT_PIN GPIO_NUM_13 // Uno D9 + #define Y_LIMIT_PIN GPIO_NUM_5 // Uno D10 + #define Z_LIMIT_PIN GPIO_NUM_19 // Uno D12 + #define LIMIT_MASK B111 + + #define PROBE_PIN GPIO_NUM_39 // Uno A5 + + // comment out #define IGNORE_CONTROL_PINS in config.h to use control pins + #define CONTROL_RESET_PIN GPIO_NUM_2 // Uno A0 + #define CONTROL_FEED_HOLD_PIN GPIO_NUM_4 // Uno A1 + #define CONTROL_CYCLE_START_PIN GPIO_NUM_35 // Uno A2 ... ESP32 needs external pullup +#endif + +#ifdef CPU_MAP_ESP32_ESC_SPINDLE + // This is an example of using a Brushless DC Hobby motor as + // a spindle motor + // See this wiki page for more info + // https://github.com/bdring/Grbl_Esp32/wiki/BESC-Spindle-Feature + + #define CPU_MAP_NAME "CPU_MAP_ESP32_ESC_SPINDLE" + + #define USE_RMT_STEPS + + #define X_STEP_PIN GPIO_NUM_12 + #define X_DIRECTION_PIN GPIO_NUM_14 + #define X_RMT_CHANNEL 0 + + #define Y_STEP_PIN GPIO_NUM_26 + #define Y_DIRECTION_PIN GPIO_NUM_15// #define Y_STEP_PIN (see versions above) + #define Y_RMT_CHANNEL 1 + + #define Z_STEP_PIN GPIO_NUM_27 + #define Z_DIRECTION_PIN GPIO_NUM_33 + #define Z_RMT_CHANNEL 2 + + // OK to comment out to use pin for other features + #define STEPPERS_DISABLE_PIN GPIO_NUM_13 + + #define SPINDLE_PWM_PIN GPIO_NUM_2 + #define SPINDLE_ENABLE_PIN GPIO_NUM_22 + #define SPINDLE_PWM_CHANNEL 0 + + // Begin RC ESC Based Spindle Information ====================== + #define SPINDLE_PWM_BIT_PRECISION 16 // 16 bit recommended for ESC (don't change) + /* + Important ESC Settings + $33=50 // Hz this is the typical good frequency for an ESC + + Determine the typical min and max pulse length of your ESC + min_pulse is typically 1ms (0.001 sec) or less + max_pulse is typically 2ms (0.002 sec) or more + + determine PWM_period. It is (1/freq) if freq = 50...period = 0.02 + + determine pulse length for min_pulse and max_pulse in percent. + + (pulse / PWM_period) + + min_pulse = (0.001 / 0.02) = 0.035 = 3.5% so ... $33 and $34 = 3.5 + max_pulse = (0.002 / .02) = 0.1 = 10% so ... $36=10 + + */ + + + // End RC ESC Based Spindle #defines =========================== + + #define X_LIMIT_PIN GPIO_NUM_17 + #define Y_LIMIT_PIN GPIO_NUM_4 + #define Z_LIMIT_PIN GPIO_NUM_16 + #define LIMIT_MASK B111 + + #define PROBE_PIN GPIO_NUM_32 + + #define CONTROL_SAFETY_DOOR_PIN GPIO_NUM_35 // needs external pullup + #define CONTROL_RESET_PIN GPIO_NUM_34 // needs external pullup + #define CONTROL_FEED_HOLD_PIN GPIO_NUM_36 // needs external pullup + #define CONTROL_CYCLE_START_PIN GPIO_NUM_39 // needs external pullup +#endif + +#ifdef CPU_MAP_PEN_LASER // The Buildlog.net pen laser controller V1 & V2 + + // For pen mode be sure to uncomment #define USE_PEN_SERVO in config.h + // For solenoid mode be sure to uncomment #define USE_PEN_SERVO in config.h + // For laser mode, you do not need to change anything + // Note: You can use all 3 modes at the same time if you want + + #define CPU_MAP_NAME "CPU_MAP_PEN_LASER" + + #define USE_RMT_STEPS + + // Pick a board version + //#define PEN_LASER_V1 + #define PEN_LASER_V2 + + #define X_STEP_PIN GPIO_NUM_12 + #define X_DIRECTION_PIN GPIO_NUM_26 + #define X_RMT_CHANNEL 0 + + + #define Y_STEP_PIN GPIO_NUM_14 + #define Y_DIRECTION_PIN GPIO_NUM_25 + #define Y_RMT_CHANNEL 1 + + #define STEPPERS_DISABLE_PIN GPIO_NUM_13 + + #ifdef PEN_LASER_V1 + #define X_LIMIT_PIN GPIO_NUM_2 + #endif + #ifdef PEN_LASER_V2 + #define X_LIMIT_PIN GPIO_NUM_15 + #endif + #define Y_LIMIT_PIN GPIO_NUM_4 + #define LIMIT_MASK B11 + + // If SPINDLE_PWM_PIN is commented out, this frees up the pin, but Grbl will still + // use a virtual spindle. Do not comment out the other parameters for the spindle. + #define SPINDLE_PWM_PIN GPIO_NUM_17 // Laser PWM + #define SPINDLE_PWM_CHANNEL 0 + #define SPINDLE_PWM_BIT_PRECISION 8 // be sure to match this with SPINDLE_PWM_MAX_VALUE + + + #define USING_SERVO // uncomment to use this feature + //#define USING_SOLENOID // uncomment to use this feature + + #ifdef USING_SERVO + #define USE_SERVO_AXES + #define SERVO_Z_PIN GPIO_NUM_27 + #define SERVO_Z_CHANNEL_NUM 3 + #define SERVO_Z_RANGE_MIN 0 + #define SERVO_Z_RANGE_MAX 10 + #endif + + #ifdef USING_SOLENOID + #define USE_PEN_SOLENOID + #define SOLENOID_PEN_PIN GPIO_NUM_16 + #define SOLENOID_CHANNEL_NUM 6 + #endif + + + // defaults + #define DEFAULT_STEP_PULSE_MICROSECONDS 3 + #define DEFAULT_STEPPER_IDLE_LOCK_TIME 250 // stay on + + #define DEFAULT_STEPPING_INVERT_MASK 0 // uint8_t + #define DEFAULT_DIRECTION_INVERT_MASK 0 // uint8_t + #define DEFAULT_INVERT_ST_ENABLE 0 // boolean + #define DEFAULT_INVERT_LIMIT_PINS 1 // boolean + #define DEFAULT_INVERT_PROBE_PIN 0 // boolean + + #define DEFAULT_STATUS_REPORT_MASK 1 + + #define DEFAULT_JUNCTION_DEVIATION 0.01 // mm + #define DEFAULT_ARC_TOLERANCE 0.002 // mm + #define DEFAULT_REPORT_INCHES 0 // false + + #define DEFAULT_SOFT_LIMIT_ENABLE 0 // false + #define DEFAULT_HARD_LIMIT_ENABLE 0 // false + + #define DEFAULT_HOMING_ENABLE 0 + #define DEFAULT_HOMING_DIR_MASK 0 // move positive dir Z, negative X,Y + #define DEFAULT_HOMING_FEED_RATE 200.0 // mm/min + #define DEFAULT_HOMING_SEEK_RATE 1000.0 // mm/min + #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k) + #define DEFAULT_HOMING_PULLOFF 3.0 // mm + + #define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm + #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm + + #define DEFAULT_LASER_MODE 0 // false + + #define DEFAULT_X_STEPS_PER_MM 80 + #define DEFAULT_Y_STEPS_PER_MM 80 + #define DEFAULT_Z_STEPS_PER_MM 100.0 // This is percent in servo mode...used for calibration + + #define DEFAULT_X_MAX_RATE 5000.0 // mm/min + #define DEFAULT_Y_MAX_RATE 5000.0 // mm/min + #define DEFAULT_Z_MAX_RATE 5000.0 // mm/min + + #define DEFAULT_X_ACCELERATION (50.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_Y_ACCELERATION (50.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 + #define DEFAULT_Z_ACCELERATION (50.0*60*60) + + #define DEFAULT_X_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value. + #define DEFAULT_Y_MAX_TRAVEL 300.0 // mm NOTE: Must be a positive value. + #define DEFAULT_Z_MAX_TRAVEL 100.0 // This is percent in servo mode...used for calibration + + +#endif + +#ifdef CPU_MAP_MIDTBOT // Buildlog.net midtbot + + #define CPU_MAP_NAME "CPU_MAP_MIDTBOT" + + #define USE_RMT_STEPS + + #define X_STEP_PIN GPIO_NUM_12 + #define Y_STEP_PIN GPIO_NUM_14 + #define X_RMT_CHANNEL 0 + + #define X_DIRECTION_PIN GPIO_NUM_26 + #define Y_DIRECTION_PIN GPIO_NUM_25 + #define Y_RMT_CHANNEL 1 + + #ifndef COREXY // maybe set in config.h + #define COREXY + #endif + + #define STEPPERS_DISABLE_PIN GPIO_NUM_13 + + #define X_LIMIT_PIN GPIO_NUM_2 + #define Y_LIMIT_PIN GPIO_NUM_4 + #define LIMIT_MASK B11 + + #ifndef USE_SERVO_AXES // maybe set in config.h + #define USE_SERVO_AXES + #endif + + #define SERVO_Z_PIN GPIO_NUM_27 + #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 + + #ifndef IGNORE_CONTROL_PINS // maybe set in config.h + #define IGNORE_CONTROL_PINS + #endif + + + + // redefine some stuff from config.h + #ifdef HOMING_CYCLE_0 + #undef HOMING_CYCLE_0 + #endif + #define HOMING_CYCLE_0 (1<. +*/ + +/* 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 \ No newline at end of file diff --git a/Grbl_Esp32-master/Grbl_Esp32/espresponse.cpp b/Grbl_Esp32-master/Grbl_Esp32/espresponse.cpp new file mode 100644 index 0000000..7c51da9 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/espresponse.cpp @@ -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 +#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 +} diff --git a/Grbl_Esp32-master/Grbl_Esp32/espresponse.h b/Grbl_Esp32-master/Grbl_Esp32/espresponse.h new file mode 100644 index 0000000..a907db0 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/espresponse.h @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/gcode.cpp b/Grbl_Esp32-master/Grbl_Esp32/gcode.cpp new file mode 100644 index 0000000..4000026 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/gcode.cpp @@ -0,0 +1,1460 @@ +/* + gcode.c - rs274/ngc parser. + 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 . +*/ + +#include "grbl.h" + +// NOTE: Max line number is defined by the g-code standard to be 99999. It seems to be an +// arbitrary value, and some GUIs may require more. So we increased it based on a max safe +// value when converting a float (7.2 digit precision)s to an integer. +#define MAX_LINE_NUMBER 10000000 +#define MAX_TOOL_NUMBER 255 // Limited by max unsigned 8-bit value + +#define AXIS_COMMAND_NONE 0 +#define AXIS_COMMAND_NON_MODAL 1 +#define AXIS_COMMAND_MOTION_MODE 2 +#define AXIS_COMMAND_TOOL_LENGTH_OFFSET 3 // *Undefined but required + +// Declare gc extern struct +parser_state_t gc_state; +parser_block_t gc_block; + +#define FAIL(status) return(status); + + +void gc_init() +{ + memset(&gc_state, 0, sizeof(parser_state_t)); + + // Load default G54 coordinate system. + if (!(settings_read_coord_data(gc_state.modal.coord_select,gc_state.coord_system))) { + report_status_message(STATUS_SETTING_READ_FAIL, CLIENT_SERIAL); + } +} + + +// Sets g-code parser position in mm. Input in steps. Called by the system abort and hard +// limit pull-off routines. +void gc_sync_position() +{ + system_convert_array_steps_to_mpos(gc_state.position,sys_position); +} + + +// Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase +// characters and signed floating point values (no whitespace). Comments and block delete +// characters have been removed. In this function, all units and positions are converted and +// exported to grbl's internal functions in terms of (mm, mm/min) and absolute machine +// coordinates, respectively. +uint8_t gc_execute_line(char *line, uint8_t client) +{ + /* ------------------------------------------------------------------------------------- + STEP 1: Initialize parser block struct and copy current g-code state modes. The parser + updates these modes and commands as the block line is parser and will only be used and + executed after successful error-checking. The parser block struct also contains a block + values struct, word tracking variables, and a non-modal commands tracker for the new + block. This struct contains all of the necessary information to execute the block. */ + + memset(&gc_block, 0, sizeof(parser_block_t)); // Initialize the parser block struct. + memcpy(&gc_block.modal,&gc_state.modal,sizeof(gc_modal_t)); // Copy current modes + + uint8_t axis_command = AXIS_COMMAND_NONE; + uint8_t axis_0, axis_1, axis_linear; + uint8_t coord_select = 0; // Tracks G10 P coordinate selection for execution + + // Initialize bitflag tracking variables for axis indices compatible operations. + uint8_t axis_words = 0; // XYZ tracking + uint8_t ijk_words = 0; // IJK tracking + + // Initialize command and value words and parser flags variables. + uint16_t command_words = 0; // Tracks G and M command words. Also used for modal group violations. + uint16_t value_words = 0; // Tracks value words. + uint8_t gc_parser_flags = GC_PARSER_NONE; + + // Determine if the line is a jogging motion or a normal g-code block. + if (line[0] == '$') { // NOTE: `$J=` already parsed when passed to this function. + // Set G1 and G94 enforced modes to ensure accurate error checks. + gc_parser_flags |= GC_PARSER_JOG_MOTION; + gc_block.modal.motion = MOTION_MODE_LINEAR; + gc_block.modal.feed_rate = FEED_RATE_MODE_UNITS_PER_MIN; +#ifdef USE_LINE_NUMBERS + gc_block.values.n = JOG_LINE_NUMBER; // Initialize default line number reported during jog. +#endif + } + + /* ------------------------------------------------------------------------------------- + STEP 2: Import all g-code words in the block line. A g-code word is a letter followed by + a number, which can either be a 'G'/'M' command or sets/assigns a command value. Also, + perform initial error-checks for command word modal group violations, for any repeated + words, and for negative values set for the value words F, N, P, T, and S. */ + + uint8_t word_bit = 0; // Bit-value for assigning tracking variables + uint8_t char_counter; + char letter; + float value; + uint8_t int_value = 0; + uint16_t mantissa = 0; + if (gc_parser_flags & GC_PARSER_JOG_MOTION) { + char_counter = 3; // Start parsing after `$J=` + } else { + char_counter = 0; + } + + while (line[char_counter] != 0) { // Loop until no more g-code words in line. + + // Import the next g-code word, expecting a letter followed by a value. Otherwise, error out. + letter = line[char_counter]; + if((letter < 'A') || (letter > 'Z')) { + FAIL(STATUS_EXPECTED_COMMAND_LETTER); // [Expected word letter] + } + char_counter++; + if (!read_float(line, &char_counter, &value)) { + FAIL(STATUS_BAD_NUMBER_FORMAT); // [Expected word value] + } + + // Convert values to smaller uint8 significand and mantissa values for parsing this word. + // NOTE: Mantissa is multiplied by 100 to catch non-integer command values. This is more + // accurate than the NIST gcode requirement of x10 when used for commands, but not quite + // accurate enough for value words that require integers to within 0.0001. This should be + // a good enough compromise and catch most all non-integer errors. To make it compliant, + // we would simply need to change the mantissa to int16, but this add compiled flash space. + // Maybe update this later. + int_value = trunc(value); + mantissa = round(100*(value - int_value)); // Compute mantissa for Gxx.x commands. + // NOTE: Rounding must be used to catch small floating point errors. + + // Check if the g-code word is supported or errors due to modal group violations or has + // been repeated in the g-code block. If ok, update the command or record its value. + switch(letter) { + + /* 'G' and 'M' Command Words: Parse commands and check for modal group violations. + NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */ + + case 'G': + // Determine 'G' command and its modal group + switch(int_value) { + case 10: + case 28: + case 30: + case 92: + // Check for G10/28/30/92 being called with G0/1/2/3/38 on same block. + // * G43.1 is also an axis command but is not explicitly defined this way. + if (mantissa == 0) { // Ignore G28.1, G30.1, and G92.1 + if (axis_command) { + FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); // [Axis word/command conflict] + } + axis_command = AXIS_COMMAND_NON_MODAL; + } + // No break. Continues to next line. + case 4: + case 53: + word_bit = MODAL_GROUP_G0; + gc_block.non_modal_command = int_value; + if ((int_value == 28) || (int_value == 30) || (int_value == 92)) { + if (!((mantissa == 0) || (mantissa == 10))) { + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); + } + gc_block.non_modal_command += mantissa; + mantissa = 0; // Set to zero to indicate valid non-integer G command. + } + break; + case 0: + case 1: + case 2: + case 3: + + case 38: + #ifndef PROBE_PIN //only allow G38 "Probe" commands if a probe pin is defined. + if (int_value == 38) { + grbl_send(CLIENT_SERIAL, "[MSG:No probe pin defined!]\r\n"); + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G command] + } + #endif + // Check for G0/1/2/3/38 being called with G10/28/30/92 on same block. + // * G43.1 is also an axis command but is not explicitly defined this way. + if (axis_command) { + FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); // [Axis word/command conflict] + } + axis_command = AXIS_COMMAND_MOTION_MODE; + // No break. Continues to next line. + case 80: + word_bit = MODAL_GROUP_G1; + gc_block.modal.motion = int_value; + if (int_value == 38) { + if (!((mantissa == 20) || (mantissa == 30) || (mantissa == 40) || (mantissa == 50))) { + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G38.x command] + } + gc_block.modal.motion += (mantissa/10)+100; + mantissa = 0; // Set to zero to indicate valid non-integer G command. + } + break; + case 17: + case 18: + case 19: + word_bit = MODAL_GROUP_G2; + gc_block.modal.plane_select = int_value - 17; + break; + case 90: + case 91: + if (mantissa == 0) { + word_bit = MODAL_GROUP_G3; + gc_block.modal.distance = int_value - 90; + } else { + word_bit = MODAL_GROUP_G4; + if ((mantissa != 10) || (int_value == 90)) { + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [G90.1 not supported] + } + mantissa = 0; // Set to zero to indicate valid non-integer G command. + // Otherwise, arc IJK incremental mode is default. G91.1 does nothing. + } + break; + case 93: + case 94: + word_bit = MODAL_GROUP_G5; + gc_block.modal.feed_rate = 94 - int_value; + break; + case 20: + case 21: + word_bit = MODAL_GROUP_G6; + gc_block.modal.units = 21 - int_value; + break; + case 40: + word_bit = MODAL_GROUP_G7; + // NOTE: Not required since cutter radius compensation is always disabled. Only here + // to support G40 commands that often appear in g-code program headers to setup defaults. + // gc_block.modal.cutter_comp = CUTTER_COMP_DISABLE; // G40 + break; + case 43: + case 49: + word_bit = MODAL_GROUP_G8; + // NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed, + // there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49 + // all are explicit axis commands, regardless if they require axis words or not. + if (axis_command) { + FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); + } // [Axis word/command conflict] } + axis_command = AXIS_COMMAND_TOOL_LENGTH_OFFSET; + if (int_value == 49) { // G49 + gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_CANCEL; + } else if (mantissa == 10) { // G43.1 + gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC; + } else { + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G43.x command] + } + mantissa = 0; // Set to zero to indicate valid non-integer G command. + break; + case 54: + case 55: + case 56: + case 57: + case 58: + case 59: + // NOTE: G59.x are not supported. (But their int_values would be 60, 61, and 62.) + word_bit = MODAL_GROUP_G12; + gc_block.modal.coord_select = int_value - 54; // Shift to array indexing. + break; + case 61: + word_bit = MODAL_GROUP_G13; + if (mantissa != 0) { + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [G61.1 not supported] + } + // gc_block.modal.control = CONTROL_MODE_EXACT_PATH; // G61 + break; + default: + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G command] + } + if (mantissa > 0) { + FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); // [Unsupported or invalid Gxx.x command] + } + // Check for more than one command per modal group violations in the current block + // NOTE: Variable 'word_bit' is always assigned, if the command is valid. + if ( bit_istrue(command_words,bit(word_bit)) ) { + FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); + } + command_words |= bit(word_bit); + break; + + case 'M': + + // Determine 'M' command and its modal group + if (mantissa > 0) { + FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); // [No Mxx.x commands] + } + switch(int_value) { + case 0: + case 1: + case 2: + case 30: + word_bit = MODAL_GROUP_M4; + switch(int_value) { + case 0: + gc_block.modal.program_flow = PROGRAM_FLOW_PAUSED; + break; // Program pause + case 1: + break; // Optional stop not supported. Ignore. + default: + gc_block.modal.program_flow = int_value; // Program end and reset + } + break; + + case 3: + case 4: + case 5: + #ifndef SPINDLE_PWM_PIN + grbl_send(CLIENT_SERIAL, "[MSG:No spindle pin defined]\r\n"); + #endif + word_bit = MODAL_GROUP_M7; + switch(int_value) { + case 3: + gc_block.modal.spindle = SPINDLE_ENABLE_CW; + break; + case 4: // Supported if SPINDLE_DIR_PIN is defined or laser mode is on. +#ifndef SPINDLE_DIR_PIN + // if laser mode is not on then this is an unsupported command + if bit_isfalse(settings.flags,BITFLAG_LASER_MODE) { + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); + break; + } +#endif + gc_block.modal.spindle = SPINDLE_ENABLE_CCW; + break; + case 5: + gc_block.modal.spindle = SPINDLE_DISABLE; + break; + } + break; + case 6: // too change + word_bit = MODAL_GROUP_M6; + gc_block.modal.tool_change = TOOL_CHANGE; + #ifdef USE_TOOL_CHANGE + //tool_change(gc_state.tool); + #endif + break; + case 7: + case 8: + case 9: + word_bit = MODAL_GROUP_M8; + switch(int_value) { +#ifdef COOLANT_MIST_PIN + case 7: + gc_block.modal.coolant = COOLANT_MIST_ENABLE; + break; +#endif +#ifdef COOLANT_FLOOD_PIN + case 8: + gc_block.modal.coolant = COOLANT_FLOOD_ENABLE; + break; +#endif + case 9: + gc_block.modal.coolant = COOLANT_DISABLE; + break; + default: + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported M command] + } + break; + case 62: + case 63: + //grbl_sendf(CLIENT_SERIAL,"M%d...\r\n", int_value); + word_bit = MODAL_GROUP_M10; + switch (int_value) { + case 62: + gc_block.modal.io_control = NON_MODAL_IO_ENABLE; + break; + case 63: + gc_block.modal.io_control = NON_MODAL_IO_DISABLE; + break; + default: + break; + } + + break; + default: + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported M command] + } + + // Check for more than one command per modal group violations in the current block + // NOTE: Variable 'word_bit' is always assigned, if the command is valid. + if ( bit_istrue(command_words,bit(word_bit)) ) { + FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); + } + command_words |= bit(word_bit); + break; + + // NOTE: All remaining letters assign values. + default: + + /* Non-Command Words: This initial parsing phase only checks for repeats of the remaining + legal g-code words and stores their value. Error-checking is performed later since some + words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands. */ + switch(letter) { +#if (N_AXIS > A_AXIS) + case 'A': + word_bit = WORD_A; + gc_block.values.xyz[A_AXIS] = value; + axis_words |= (1< B_AXIS) + case 'B': + word_bit = WORD_B; + gc_block.values.xyz[B_AXIS] = value; + axis_words |= (1< C_AXIS) + case 'C': + word_bit = WORD_C; + gc_block.values.xyz[C_AXIS] = value; + axis_words |= (1< MAX_TOOL_NUMBER) { + FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); + } + grbl_sendf(CLIENT_ALL, "[MSG:Tool No: %d]\r\n", int_value); + gc_state.tool = int_value; + break; + case 'X': + word_bit = WORD_X; + gc_block.values.xyz[X_AXIS] = value; + axis_words |= (1< MAX_LINE_NUMBER) { + FAIL(STATUS_GCODE_INVALID_LINE_NUMBER); // [Exceeds max line number] + } + } + // bit_false(value_words,bit(WORD_N)); // NOTE: Single-meaning value word. Set at end of error-checking. + + // Track for unused words at the end of error-checking. + // NOTE: Single-meaning value words are removed all at once at the end of error-checking, because + // they are always used when present. This was done to save a few bytes of flash. For clarity, the + // single-meaning value words may be removed as they are used. Also, axis words are treated in the + // same way. If there is an explicit/implicit axis command, XYZ words are always used and are + // are removed at the end of error-checking. + + // [1. Comments ]: MSG's NOT SUPPORTED. Comment handling performed by protocol. + + // [2. Set feed rate mode ]: G93 F word missing with G1,G2/3 active, implicitly or explicitly. Feed rate + // is not defined after switching to G94 from G93. + // NOTE: For jogging, ignore prior feed rate mode. Enforce G94 and check for required F word. + if (gc_parser_flags & GC_PARSER_JOG_MOTION) { + if (bit_isfalse(value_words,bit(WORD_F))) { + FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); + } + if (gc_block.modal.units == UNITS_MODE_INCHES) { + gc_block.values.f *= MM_PER_INCH; + } + } else { + if (gc_block.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { // = G93 + // NOTE: G38 can also operate in inverse time, but is undefined as an error. Missing F word check added here. + if (axis_command == AXIS_COMMAND_MOTION_MODE) { + if ((gc_block.modal.motion != MOTION_MODE_NONE) || (gc_block.modal.motion != MOTION_MODE_SEEK)) { + if (bit_isfalse(value_words,bit(WORD_F))) { + FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); // [F word missing] + } + } + } + // NOTE: It seems redundant to check for an F word to be passed after switching from G94 to G93. We would + // accomplish the exact same thing if the feed rate value is always reset to zero and undefined after each + // inverse time block, since the commands that use this value already perform undefined checks. This would + // also allow other commands, following this switch, to execute and not error out needlessly. This code is + // combined with the above feed rate mode and the below set feed rate error-checking. + + // [3. Set feed rate ]: F is negative (done.) + // - In inverse time mode: Always implicitly zero the feed rate value before and after block completion. + // NOTE: If in G93 mode or switched into it from G94, just keep F value as initialized zero or passed F word + // value in the block. If no F word is passed with a motion command that requires a feed rate, this will error + // out in the motion modes error-checking. However, if no F word is passed with NO motion command that requires + // a feed rate, we simply move on and the state feed rate value gets updated to zero and remains undefined. + } else { // = G94 + // - In units per mm mode: If F word passed, ensure value is in mm/min, otherwise push last state value. + if (gc_state.modal.feed_rate == FEED_RATE_MODE_UNITS_PER_MIN) { // Last state is also G94 + if (bit_istrue(value_words,bit(WORD_F))) { + if (gc_block.modal.units == UNITS_MODE_INCHES) { + gc_block.values.f *= MM_PER_INCH; + } + } else { + gc_block.values.f = gc_state.feed_rate; // Push last state feed rate + } + } // Else, switching to G94 from G93, so don't push last state feed rate. Its undefined or the passed F word value. + } + } + // bit_false(value_words,bit(WORD_F)); // NOTE: Single-meaning value word. Set at end of error-checking. + + // [4. Set spindle speed ]: S is negative (done.) + if (bit_isfalse(value_words,bit(WORD_S))) { + gc_block.values.s = gc_state.spindle_speed; + } + // bit_false(value_words,bit(WORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking. + + // [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool value. + // bit_false(value_words,bit(WORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking. + + // [6. Change tool ]: N/A + // [7. Spindle control ]: N/A + // [8. Coolant control ]: N/A + // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED. + + // [10. Dwell ]: P value missing. P is negative (done.) NOTE: See below. + if (gc_block.non_modal_command == NON_MODAL_DWELL) { + if (bit_isfalse(value_words,bit(WORD_P))) { + FAIL(STATUS_GCODE_VALUE_WORD_MISSING); // [P word missing] + } + bit_false(value_words,bit(WORD_P)); + } + + if ( (gc_block.modal.io_control == NON_MODAL_IO_ENABLE) || (gc_block.modal.io_control == NON_MODAL_IO_DISABLE)) { + if (bit_isfalse(value_words,bit(WORD_P))) { + FAIL(STATUS_GCODE_VALUE_WORD_MISSING); // [P word missing] + } + bit_false(value_words,bit(WORD_P)); + } + + // [11. Set active plane ]: N/A + switch (gc_block.modal.plane_select) { + case PLANE_SELECT_XY: + axis_0 = X_AXIS; + axis_1 = Y_AXIS; + axis_linear = Z_AXIS; + break; + case PLANE_SELECT_ZX: + axis_0 = Z_AXIS; + axis_1 = X_AXIS; + axis_linear = Y_AXIS; + break; + default: // case PLANE_SELECT_YZ: + axis_0 = Y_AXIS; + axis_1 = Z_AXIS; + axis_linear = X_AXIS; + } + + // [12. Set length units ]: N/A + // Pre-convert XYZ coordinate values to millimeters, if applicable. + uint8_t idx; + if (gc_block.modal.units == UNITS_MODE_INCHES) { + for (idx=0; idx N_COORDINATE_SYSTEM) { + FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); // [Greater than N sys] + } + if (gc_state.modal.coord_select != gc_block.modal.coord_select) { + if (!(settings_read_coord_data(gc_block.modal.coord_select,block_coord_system))) { + FAIL(STATUS_SETTING_READ_FAIL); + } + } + } + + // [16. Set path control mode ]: N/A. Only G61. G61.1 and G64 NOT SUPPORTED. + // [17. Set distance mode ]: N/A. Only G91.1. G90.1 NOT SUPPORTED. + // [18. Set retract mode ]: NOT SUPPORTED. + + // [19. Remaining non-modal actions ]: Check go to predefined position, set G10, or set axis offsets. + // NOTE: We need to separate the non-modal commands that are axis word-using (G10/G28/G30/G92), as these + // commands all treat axis words differently. G10 as absolute offsets or computes current position as + // the axis value, G92 similarly to G10 L20, and G28/30 as an intermediate target position that observes + // all the current coordinate system and G92 offsets. + switch (gc_block.non_modal_command) { + case NON_MODAL_SET_COORDINATE_DATA: + // [G10 Errors]: L missing and is not 2 or 20. P word missing. (Negative P value done.) + // [G10 L2 Errors]: R word NOT SUPPORTED. P value not 0 to nCoordSys(max 9). Axis words missing. + // [G10 L20 Errors]: P must be 0 to nCoordSys(max 9). Axis words missing. + if (!axis_words) { + FAIL(STATUS_GCODE_NO_AXIS_WORDS) + }; // [No axis words] + if (bit_isfalse(value_words,((1< N_COORDINATE_SYSTEM) { + FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); // [Greater than N sys] + } + if (gc_block.values.l != 20) { + if (gc_block.values.l == 2) { + if (bit_istrue(value_words,bit(WORD_R))) { + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [G10 L2 R not supported] + } + } else { + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported L] + } + } + bit_false(value_words,(bit(WORD_L)|bit(WORD_P))); + + // Determine coordinate system to change and try to load from EEPROM. + if (coord_select > 0) { + coord_select--; // Adjust P1-P6 index to EEPROM coordinate data indexing. + } else { + coord_select = gc_block.modal.coord_select; // Index P0 as the active coordinate system + } + + // NOTE: Store parameter data in IJK values. By rule, they are not in use with this command. + // FIXME: Instead of IJK, we'd better use: float vector[N_AXIS]; // [DG] + if (!settings_read_coord_data(coord_select,gc_block.values.ijk)) { + FAIL(STATUS_SETTING_READ_FAIL); // [EEPROM read fail] + } + + // Pre-calculate the coordinate data changes. + for (idx=0; idx WCS = MPos - G92 - TLO - WPos + gc_block.values.ijk[idx] = gc_state.position[idx]-gc_state.coord_offset[idx]-gc_block.values.xyz[idx]; + if (idx == TOOL_LENGTH_OFFSET_AXIS) { + gc_block.values.ijk[idx] -= gc_state.tool_length_offset; + } + } else { + // L2: Update coordinate system axis to programmed value. + gc_block.values.ijk[idx] = gc_block.values.xyz[idx]; + } + } // Else, keep current stored value. + } + break; + case NON_MODAL_SET_COORDINATE_OFFSET: + // [G92 Errors]: No axis words. + if (!axis_words) { + FAIL(STATUS_GCODE_NO_AXIS_WORDS); // [No axis words] + } + + // Update axes defined only in block. Offsets current system to defined value. Does not update when + // active coordinate system is selected, but is still active unless G92.1 disables it. + for (idx=0; idx G92 = MPos - WCS - TLO - WPos + gc_block.values.xyz[idx] = gc_state.position[idx]-block_coord_system[idx]-gc_block.values.xyz[idx]; + if (idx == TOOL_LENGTH_OFFSET_AXIS) { + gc_block.values.xyz[idx] -= gc_state.tool_length_offset; + } + } else { + gc_block.values.xyz[idx] = gc_state.coord_offset[idx]; + } + } + break; + + default: + + // At this point, the rest of the explicit axis commands treat the axis values as the traditional + // target position with the coordinate system offsets, G92 offsets, absolute override, and distance + // modes applied. This includes the motion mode commands. We can now pre-compute the target position. + // NOTE: Tool offsets may be appended to these conversions when/if this feature is added. + if (axis_command != AXIS_COMMAND_TOOL_LENGTH_OFFSET ) { // TLO block any axis command. + if (axis_words) { + for (idx=0; idx C -----------------+--------------- T <- [x,y] + | <------ d/2 ---->| + + C - Current position + T - Target position + O - center of circle that pass through both C and T + d - distance from C to T + r - designated radius + h - distance from center of CT to O + + Expanding the equations: + + d -> sqrt(x^2 + y^2) + h -> sqrt(4 * r^2 - x^2 - y^2)/2 + i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 + j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 + + Which can be written: + + i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 + j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 + + Which we for size and speed reasons optimize to: + + h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2) + i = (x - (y * h_x2_div_d))/2 + j = (y + (x * h_x2_div_d))/2 + */ + + // First, use h_x2_div_d to compute 4*h^2 to check if it is negative or r is smaller + // than d. If so, the sqrt of a negative number is complex and error out. + float h_x2_div_d = 4.0 * gc_block.values.r*gc_block.values.r - x*x - y*y; + + if (h_x2_div_d < 0) { + FAIL(STATUS_GCODE_ARC_RADIUS_ERROR); // [Arc radius error] + } + + // Finish computing h_x2_div_d. + h_x2_div_d = -sqrt(h_x2_div_d)/hypot_f(x,y); // == -(h * 2 / d) + // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below) + if (gc_block.modal.motion == MOTION_MODE_CCW_ARC) { + h_x2_div_d = -h_x2_div_d; + } + + /* The counter clockwise circle lies to the left of the target direction. When offset is positive, + the left hand circle will be generated - when it is negative the right hand circle is generated. + + T <-- Target position + + ^ + Clockwise circles with this center | Clockwise circles with this center will have + will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing! + \ | / + center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative + | + | + + C <-- Current position + */ + // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!), + // even though it is advised against ever generating such circles in a single line of g-code. By + // inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of + // travel and thus we get the unadvisably long arcs as prescribed. + if (gc_block.values.r < 0) { + h_x2_div_d = -h_x2_div_d; + gc_block.values.r = -gc_block.values.r; // Finished with r. Set to positive for mc_arc + } + // Complete the operation by calculating the actual center of the arc + gc_block.values.ijk[axis_0] = 0.5*(x-(y*h_x2_div_d)); + gc_block.values.ijk[axis_1] = 0.5*(y+(x*h_x2_div_d)); + + } else { // Arc Center Format Offset Mode + if (!(ijk_words & (bit(axis_0)|bit(axis_1)))) { + FAIL(STATUS_GCODE_NO_OFFSETS_IN_PLANE); // [No offsets in plane] + } + bit_false(value_words,(bit(WORD_I)|bit(WORD_J)|bit(WORD_K))); + + // Convert IJK values to proper units. + if (gc_block.modal.units == UNITS_MODE_INCHES) { + for (idx=0; idx 0.005) { + if (delta_r > 0.5) { + FAIL(STATUS_GCODE_INVALID_TARGET); // [Arc definition error] > 0.5mm + } + if (delta_r > (0.001*gc_block.values.r)) { + FAIL(STATUS_GCODE_INVALID_TARGET); // [Arc definition error] > 0.005mm AND 0.1% radius + } + } + } + break; + case MOTION_MODE_PROBE_TOWARD_NO_ERROR: + case MOTION_MODE_PROBE_AWAY_NO_ERROR: + gc_parser_flags |= GC_PARSER_PROBE_IS_NO_ERROR; // No break intentional. + case MOTION_MODE_PROBE_TOWARD: + case MOTION_MODE_PROBE_AWAY: + if ((gc_block.modal.motion == MOTION_MODE_PROBE_AWAY) || + (gc_block.modal.motion == MOTION_MODE_PROBE_AWAY_NO_ERROR)) { + gc_parser_flags |= GC_PARSER_PROBE_IS_AWAY; + } + // [G38 Errors]: Target is same current. No axis words. Cutter compensation is enabled. Feed rate + // is undefined. Probe is triggered. NOTE: Probe check moved to probe cycle. Instead of returning + // an error, it issues an alarm to prevent further motion to the probe. It's also done there to + // allow the planner buffer to empty and move off the probe trigger before another probing cycle. + if (!axis_words) { + FAIL(STATUS_GCODE_NO_AXIS_WORDS); // [No axis words] + } + if (isequal_position_vector(gc_state.position, gc_block.values.xyz)) { + FAIL(STATUS_GCODE_INVALID_TARGET); // [Invalid target] + } + break; + } + } + } + + // [21. Program flow ]: No error checks required. + + // [0. Non-specific error-checks]: Complete unused value words check, i.e. IJK used when in arc + // radius mode, or axis words that aren't used in the block. + if (gc_parser_flags & GC_PARSER_JOG_MOTION) { + // Jogging only uses the F feed rate and XYZ value words. N is valid, but S and T are invalid. + bit_false(value_words,(bit(WORD_N)|bit(WORD_F))); + } else { + bit_false(value_words,(bit(WORD_N)|bit(WORD_F)|bit(WORD_S)|bit(WORD_T))); // Remove single-meaning value words. + } + + if (axis_command) { + bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z)|bit(WORD_A)|bit(WORD_B)|bit(WORD_C))); // Remove axis words. + } + if (value_words) { + FAIL(STATUS_GCODE_UNUSED_WORDS); // [Unused words] + } + + /* ------------------------------------------------------------------------------------- + STEP 4: EXECUTE!! + Assumes that all error-checking has been completed and no failure modes exist. We just + need to update the state and execute the block according to the order-of-execution. + */ + + // Initialize planner data struct for motion blocks. + plan_line_data_t plan_data; + plan_line_data_t *pl_data = &plan_data; + memset(pl_data,0,sizeof(plan_line_data_t)); // Zero pl_data struct + + // Intercept jog commands and complete error checking for valid jog commands and execute. + // NOTE: G-code parser state is not updated, except the position to ensure sequential jog + // targets are computed correctly. The final parser position after a jog is updated in + // protocol_execute_realtime() when jogging completes or is canceled. + if (gc_parser_flags & GC_PARSER_JOG_MOTION) { + // Only distance and unit modal commands and G53 absolute override command are allowed. + // NOTE: Feed rate word and axis word checks have already been performed in STEP 3. + if (command_words & ~(bit(MODAL_GROUP_G3) | bit(MODAL_GROUP_G6) | bit(MODAL_GROUP_G0)) ) { + FAIL(STATUS_INVALID_JOG_COMMAND) + }; + if (!(gc_block.non_modal_command == NON_MODAL_ABSOLUTE_OVERRIDE || gc_block.non_modal_command == NON_MODAL_NO_ACTION)) { + FAIL(STATUS_INVALID_JOG_COMMAND); + } + + // Initialize planner data to current spindle and coolant modal state. + pl_data->spindle_speed = gc_state.spindle_speed; + plan_data.condition = (gc_state.modal.spindle | gc_state.modal.coolant); + + uint8_t status = jog_execute(&plan_data, &gc_block); + if (status == STATUS_OK) { + memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); + } + return(status); + } + + // If in laser mode, setup laser power based on current and past parser conditions. + if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) { + if ( !((gc_block.modal.motion == MOTION_MODE_LINEAR) || (gc_block.modal.motion == MOTION_MODE_CW_ARC) + || (gc_block.modal.motion == MOTION_MODE_CCW_ARC)) ) { + gc_parser_flags |= GC_PARSER_LASER_DISABLE; + } + + // Any motion mode with axis words is allowed to be passed from a spindle speed update. + // NOTE: G1 and G0 without axis words sets axis_command to none. G28/30 are intentionally omitted. + // TODO: Check sync conditions for M3 enabled motions that don't enter the planner. (zero length). + if (axis_words && (axis_command == AXIS_COMMAND_MOTION_MODE)) { + gc_parser_flags |= GC_PARSER_LASER_ISMOTION; + } else { + // M3 constant power laser requires planner syncs to update the laser when changing between + // a G1/2/3 motion mode state and vice versa when there is no motion in the line. + if (gc_state.modal.spindle == SPINDLE_ENABLE_CW) { + if ((gc_state.modal.motion == MOTION_MODE_LINEAR) || (gc_state.modal.motion == MOTION_MODE_CW_ARC) + || (gc_state.modal.motion == MOTION_MODE_CCW_ARC)) { + if (bit_istrue(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { + gc_parser_flags |= GC_PARSER_LASER_FORCE_SYNC; // Change from G1/2/3 motion mode. + } + } else { + // When changing to a G1 motion mode without axis words from a non-G1/2/3 motion mode. + if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { + gc_parser_flags |= GC_PARSER_LASER_FORCE_SYNC; + } + } + } + } + } + + // [0. Non-specific/common error-checks and miscellaneous setup]: + // NOTE: If no line number is present, the value is zero. + gc_state.line_number = gc_block.values.n; +#ifdef USE_LINE_NUMBERS + pl_data->line_number = gc_state.line_number; // Record data for planner use. +#endif + + // [1. Comments feedback ]: NOT SUPPORTED + + // [2. Set feed rate mode ]: + gc_state.modal.feed_rate = gc_block.modal.feed_rate; + if (gc_state.modal.feed_rate) { + pl_data->condition |= PL_COND_FLAG_INVERSE_TIME; // Set condition flag for planner use. + } + + // [3. Set feed rate ]: + gc_state.feed_rate = gc_block.values.f; // Always copy this value. See feed rate error-checking. + pl_data->feed_rate = gc_state.feed_rate; // Record data for planner use. + + // [4. Set spindle speed ]: + if ((gc_state.spindle_speed != gc_block.values.s) || bit_istrue(gc_parser_flags,GC_PARSER_LASER_FORCE_SYNC)) { + if (gc_state.modal.spindle != SPINDLE_DISABLE) { +#ifdef VARIABLE_SPINDLE + if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_ISMOTION)) { + if (bit_istrue(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { + spindle_sync(gc_state.modal.spindle, 0.0); + } else { + spindle_sync(gc_state.modal.spindle, gc_block.values.s); + } + } +#else + spindle_sync(gc_state.modal.spindle, 0.0); +#endif + } + gc_state.spindle_speed = gc_block.values.s; // Update spindle speed state. + } + // NOTE: Pass zero spindle speed for all restricted laser motions. + if (bit_isfalse(gc_parser_flags,GC_PARSER_LASER_DISABLE)) { + pl_data->spindle_speed = gc_state.spindle_speed; // Record data for planner use. + } // else { pl_data->spindle_speed = 0.0; } // Initialized as zero already. + + // [5. Select tool ]: NOT SUPPORTED. Only tracks tool value. + // gc_state.tool = gc_block.values.t; + + // [6. Change tool ]: NOT SUPPORTED + if (gc_block.modal.tool_change == TOOL_CHANGE) { + #ifdef USE_TOOL_CHANGE + user_tool_change(gc_state.tool); + #endif + } + + // [7. Spindle control ]: + if (gc_state.modal.spindle != gc_block.modal.spindle) { + // Update spindle control and apply spindle speed when enabling it in this block. + // NOTE: All spindle state changes are synced, even in laser mode. Also, pl_data, + // rather than gc_state, is used to manage laser state for non-laser motions. + spindle_sync(gc_block.modal.spindle, pl_data->spindle_speed); + gc_state.modal.spindle = gc_block.modal.spindle; + } + pl_data->condition |= gc_state.modal.spindle; // Set condition flag for planner use. + + // [8. Coolant control ]: + if (gc_state.modal.coolant != gc_block.modal.coolant) { + // NOTE: Coolant M-codes are modal. Only one command per line is allowed. But, multiple states + // can exist at the same time, while coolant disable clears all states. + coolant_sync(gc_block.modal.coolant); + if (gc_block.modal.coolant == COOLANT_DISABLE) { + gc_state.modal.coolant = COOLANT_DISABLE; + } else { + gc_state.modal.coolant |= gc_block.modal.coolant; + } + } + pl_data->condition |= gc_state.modal.coolant; // Set condition flag for planner use. + + // turn on/off an i/o pin + if ( (gc_block.modal.io_control == NON_MODAL_IO_ENABLE) || (gc_block.modal.io_control == NON_MODAL_IO_DISABLE) ) { + if (gc_block.values.p <= MAX_USER_DIGITAL_PIN) { + sys_io_control(1<<(int)gc_block.values.p, (gc_block.modal.io_control == NON_MODAL_IO_ENABLE)); + } + else { + FAIL(STATUS_P_PARAM_MAX_EXCEEDED); + } + } + + // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED. Always enabled. + + // [10. Dwell ]: + if (gc_block.non_modal_command == NON_MODAL_DWELL) { + mc_dwell(gc_block.values.p); + } + + // [11. Set active plane ]: + gc_state.modal.plane_select = gc_block.modal.plane_select; + + // [12. Set length units ]: + gc_state.modal.units = gc_block.modal.units; + + // [13. Cutter radius compensation ]: G41/42 NOT SUPPORTED + // gc_state.modal.cutter_comp = gc_block.modal.cutter_comp; // NOTE: Not needed since always disabled. + + // [14. Cutter length compensation ]: G43.1 and G49 supported. G43 NOT SUPPORTED. + // NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms + // of execution. The error-checking step would simply load the offset value into the correct + // axis of the block XYZ value array. + if (axis_command == AXIS_COMMAND_TOOL_LENGTH_OFFSET ) { // Indicates a change. + gc_state.modal.tool_length = gc_block.modal.tool_length; + if (gc_state.modal.tool_length == TOOL_LENGTH_OFFSET_CANCEL) { // G49 + gc_block.values.xyz[TOOL_LENGTH_OFFSET_AXIS] = 0.0; + } // else G43.1 + if ( gc_state.tool_length_offset != gc_block.values.xyz[TOOL_LENGTH_OFFSET_AXIS] ) { + gc_state.tool_length_offset = gc_block.values.xyz[TOOL_LENGTH_OFFSET_AXIS]; + system_flag_wco_change(); + } + } + + // [15. Coordinate system selection ]: + if (gc_state.modal.coord_select != gc_block.modal.coord_select) { + gc_state.modal.coord_select = gc_block.modal.coord_select; + memcpy(gc_state.coord_system,block_coord_system,N_AXIS*sizeof(float)); + system_flag_wco_change(); + } + + // [16. Set path control mode ]: G61.1/G64 NOT SUPPORTED + // gc_state.modal.control = gc_block.modal.control; // NOTE: Always default. + + // [17. Set distance mode ]: + gc_state.modal.distance = gc_block.modal.distance; + + // [18. Set retract mode ]: NOT SUPPORTED + + // [19. Go to predefined position, Set G10, or Set axis offsets ]: + switch(gc_block.non_modal_command) { + case NON_MODAL_SET_COORDINATE_DATA: + settings_write_coord_data(coord_select,gc_block.values.ijk); + // Update system coordinate system if currently active. + if (gc_state.modal.coord_select == coord_select) { + memcpy(gc_state.coord_system,gc_block.values.ijk,N_AXIS*sizeof(float)); + system_flag_wco_change(); + } + break; + case NON_MODAL_GO_HOME_0: + case NON_MODAL_GO_HOME_1: + // Move to intermediate position before going home. Obeys current coordinate system and offsets + // and absolute and incremental modes. + pl_data->condition |= PL_COND_FLAG_RAPID_MOTION; // Set rapid motion condition flag. + if (axis_command) { + mc_line(gc_block.values.xyz, pl_data); // kinematics kinematics not used for homing righ now + } + mc_line(gc_block.values.ijk, pl_data); + memcpy(gc_state.position, gc_block.values.ijk, N_AXIS*sizeof(float)); + break; + case NON_MODAL_SET_HOME_0: + settings_write_coord_data(SETTING_INDEX_G28,gc_state.position); + break; + case NON_MODAL_SET_HOME_1: + settings_write_coord_data(SETTING_INDEX_G30,gc_state.position); + break; + case NON_MODAL_SET_COORDINATE_OFFSET: + memcpy(gc_state.coord_offset,gc_block.values.xyz,sizeof(gc_block.values.xyz)); + system_flag_wco_change(); + break; + case NON_MODAL_RESET_COORDINATE_OFFSET: + clear_vector(gc_state.coord_offset); // Disable G92 offsets by zeroing offset vector. + system_flag_wco_change(); + break; + } + + + // [20. Motion modes ]: + // NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes. + // Enter motion modes only if there are axis words or a motion mode command word in the block. + gc_state.modal.motion = gc_block.modal.motion; + if (gc_state.modal.motion != MOTION_MODE_NONE) { + if (axis_command == AXIS_COMMAND_MOTION_MODE) { + uint8_t gc_update_pos = GC_UPDATE_POS_TARGET; + if (gc_state.modal.motion == MOTION_MODE_LINEAR) { + //mc_line(gc_block.values.xyz, pl_data); + mc_line_kins(gc_block.values.xyz, pl_data, gc_state.position); + } else if (gc_state.modal.motion == MOTION_MODE_SEEK) { + pl_data->condition |= PL_COND_FLAG_RAPID_MOTION; // Set rapid motion condition flag. + //mc_line(gc_block.values.xyz, pl_data); + mc_line_kins(gc_block.values.xyz, pl_data, gc_state.position); + } else if ((gc_state.modal.motion == MOTION_MODE_CW_ARC) || (gc_state.modal.motion == MOTION_MODE_CCW_ARC)) { + mc_arc(gc_block.values.xyz, pl_data, gc_state.position, gc_block.values.ijk, gc_block.values.r, + axis_0, axis_1, axis_linear, bit_istrue(gc_parser_flags,GC_PARSER_ARC_IS_CLOCKWISE)); + } else { + // NOTE: gc_block.values.xyz is returned from mc_probe_cycle with the updated position value. So + // upon a successful probing cycle, the machine position and the returned value should be the same. +#ifndef ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES + pl_data->condition |= PL_COND_FLAG_NO_FEED_OVERRIDE; +#endif + gc_update_pos = mc_probe_cycle(gc_block.values.xyz, pl_data, gc_parser_flags); + } + + // As far as the parser is concerned, the position is now == target. In reality the + // motion control system might still be processing the action and the real tool position + // in any intermediate location. + if (gc_update_pos == GC_UPDATE_POS_TARGET) { + memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); // gc_state.position[] = gc_block.values.xyz[] + } else if (gc_update_pos == GC_UPDATE_POS_SYSTEM) { + gc_sync_position(); // gc_state.position[] = sys_position + } // == GC_UPDATE_POS_NONE + } + } + + // [21. Program flow ]: + // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may + // refill and can only be resumed by the cycle start run-time command. + gc_state.modal.program_flow = gc_block.modal.program_flow; + if (gc_state.modal.program_flow) { + protocol_buffer_synchronize(); // Sync and finish all remaining buffered motions before moving on. + if (gc_state.modal.program_flow == PROGRAM_FLOW_PAUSED) { + if (sys.state != STATE_CHECK_MODE) { + system_set_exec_state_flag(EXEC_FEED_HOLD); // Use feed hold for program pause. + protocol_execute_realtime(); // Execute suspend. + } + } else { // == PROGRAM_FLOW_COMPLETED + // Upon program complete, only a subset of g-codes reset to certain defaults, according to + // LinuxCNC's program end descriptions and testing. Only modal groups [G-code 1,2,3,5,7,12] + // and [M-code 7,8,9] reset to [G1,G17,G90,G94,G40,G54,M5,M9,M48]. The remaining modal groups + // [G-code 4,6,8,10,13,14,15] and [M-code 4,5,6] and the modal words [F,S,T,H] do not reset. + gc_state.modal.motion = MOTION_MODE_LINEAR; + gc_state.modal.plane_select = PLANE_SELECT_XY; + gc_state.modal.distance = DISTANCE_MODE_ABSOLUTE; + gc_state.modal.feed_rate = FEED_RATE_MODE_UNITS_PER_MIN; + // gc_state.modal.cutter_comp = CUTTER_COMP_DISABLE; // Not supported. + gc_state.modal.coord_select = 0; // G54 + gc_state.modal.spindle = SPINDLE_DISABLE; + gc_state.modal.coolant = COOLANT_DISABLE; + // gc_state.modal.override = OVERRIDE_DISABLE; // Not supported. + +#ifdef RESTORE_OVERRIDES_AFTER_PROGRAM_END + sys.f_override = DEFAULT_FEED_OVERRIDE; + sys.r_override = DEFAULT_RAPID_OVERRIDE; + sys.spindle_speed_ovr = DEFAULT_SPINDLE_SPEED_OVERRIDE; +#endif + + // Execute coordinate change and spindle/coolant stop. + if (sys.state != STATE_CHECK_MODE) { + if (!(settings_read_coord_data(gc_state.modal.coord_select,gc_state.coord_system))) { + FAIL(STATUS_SETTING_READ_FAIL); + } + system_flag_wco_change(); // Set to refresh immediately just in case something altered. + spindle_set_state(SPINDLE_DISABLE,0.0); + coolant_set_state(COOLANT_DISABLE); + } + report_feedback_message(MESSAGE_PROGRAM_END); + #ifdef USE_M30 + user_m30(); + #endif + } + gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; // Reset program flow. + } + + // TODO: % to denote start of program. + + return(STATUS_OK); +} + + +/* + Not supported: + + - Canned cycles + - Tool radius compensation + - A,B,C-axes + - Evaluation of expressions + - Variables + - Override control (TBD) + - Tool changes + - Switches + + (*) Indicates optional parameter, enabled through config.h and re-compile + group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets) + group 1 = {G81 - G89} (Motion modes: Canned cycles) + group 4 = {M1} (Optional stop, ignored) + group 6 = {M6} (Tool change) + group 7 = {G41, G42} cutter radius compensation (G40 is supported) + group 8 = {G43} tool length offset (G43.1/G49 are supported) + group 8 = {M7*} enable mist coolant (* Compile-option) + group 9 = {M48, M49} enable/disable feed and speed override switches + group 10 = {G98, G99} return mode canned cycles + group 13 = {G61.1, G64} path control mode (G61 is supported) +*/ diff --git a/Grbl_Esp32-master/Grbl_Esp32/gcode.h b/Grbl_Esp32-master/Grbl_Esp32/gcode.h new file mode 100644 index 0000000..f5f3cae --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/gcode.h @@ -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 . +*/ +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/grbl.h b/Grbl_Esp32-master/Grbl_Esp32/grbl.h new file mode 100644 index 0000000..02b5230 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/grbl.h @@ -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 . +*/ + +// Grbl versioning system +#define GRBL_VERSION "1.1f" +#define GRBL_VERSION_BUILD "20191208" + +//#include +#include +#include +#include +#include +#include + +#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 + diff --git a/Grbl_Esp32-master/Grbl_Esp32/grbl_eeprom.cpp b/Grbl_Esp32-master/Grbl_Esp32/grbl_eeprom.cpp new file mode 100644 index 0000000..f8cfe49 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/grbl_eeprom.cpp @@ -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 . +*/ + +#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)); +} diff --git a/Grbl_Esp32-master/Grbl_Esp32/grbl_eeprom.h b/Grbl_Esp32-master/Grbl_Esp32/grbl_eeprom.h new file mode 100644 index 0000000..3256cc7 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/grbl_eeprom.h @@ -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 . +*/ + +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/grbl_limits.cpp b/Grbl_Esp32-master/Grbl_Esp32/grbl_limits.cpp new file mode 100644 index 0000000..e0d6659 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/grbl_limits.cpp @@ -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 . +*/ + +#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; idxfeed_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 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. +*/ + +#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 \ No newline at end of file diff --git a/Grbl_Esp32-master/Grbl_Esp32/grbl_sd.cpp b/Grbl_Esp32-master/Grbl_Esp32/grbl_sd.cpp new file mode 100644 index 0000000..85b2022 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/grbl_sd.cpp @@ -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 . +*/ + +#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; + } +} + + diff --git a/Grbl_Esp32-master/Grbl_Esp32/grbl_sd.h b/Grbl_Esp32-master/Grbl_Esp32/grbl_sd.h new file mode 100644 index 0000000..9ac285b --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/grbl_sd.h @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/grbl_trinamic.cpp b/Grbl_Esp32-master/Grbl_Esp32/grbl_trinamic.cpp new file mode 100644 index 0000000..da662ef --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/grbl_trinamic.cpp @@ -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 . +*/ +#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 \ No newline at end of file diff --git a/Grbl_Esp32-master/Grbl_Esp32/grbl_trinamic.h b/Grbl_Esp32-master/Grbl_Esp32/grbl_trinamic.h new file mode 100644 index 0000000..9723124 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/grbl_trinamic.h @@ -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 . +*/ + +#ifndef GRBL_TRINAMIC_h + #define GRBL_TRINAMIC_h + +#include "grbl.h" + +#ifdef USE_TRINAMIC + #include // https://github.com/teemuatlut/TMCStepper + void Trinamic_Init(); + void trinamic_change_settings(); +#endif + +#endif \ No newline at end of file diff --git a/Grbl_Esp32-master/Grbl_Esp32/grbl_unipolar.cpp b/Grbl_Esp32-master/Grbl_Esp32/grbl_unipolar.cpp new file mode 100644 index 0000000..aaef076 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/grbl_unipolar.cpp @@ -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 . + + 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<. + + 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 \ No newline at end of file diff --git a/Grbl_Esp32-master/Grbl_Esp32/inputbuffer.cpp b/Grbl_Esp32-master/Grbl_Esp32/inputbuffer.cpp new file mode 100644 index 0000000..f336155 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/inputbuffer.cpp @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/inputbuffer.h b/Grbl_Esp32-master/Grbl_Esp32/inputbuffer.h new file mode 100644 index 0000000..9986d6a --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/inputbuffer.h @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/jog.cpp b/Grbl_Esp32-master/Grbl_Esp32/jog.cpp new file mode 100644 index 0000000..7b6b81b --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/jog.cpp @@ -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 . +*/ + +#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); +} diff --git a/Grbl_Esp32-master/Grbl_Esp32/jog.h b/Grbl_Esp32-master/Grbl_Esp32/jog.h new file mode 100644 index 0000000..07bddba --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/jog.h @@ -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 . +*/ + +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/motion_control.cpp b/Grbl_Esp32-master/Grbl_Esp32/motion_control.cpp new file mode 100644 index 0000000..fa23fca --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/motion_control.cpp @@ -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 . +*/ + +#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. +*/ + + + +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/nofile.h b/Grbl_Esp32-master/Grbl_Esp32/nofile.h new file mode 100644 index 0000000..7c34dd3 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/nofile.h @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/notifications_service.cpp b/Grbl_Esp32-master/Grbl_Esp32/notifications_service.cpp new file mode 100644 index 0000000..f05308b --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/notifications_service.cpp @@ -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 +#include +#include +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/notifications_service.h b/Grbl_Esp32-master/Grbl_Esp32/notifications_service.h new file mode 100644 index 0000000..5f6c6b9 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/notifications_service.h @@ -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 + diff --git a/Grbl_Esp32-master/Grbl_Esp32/nuts_bolts.cpp b/Grbl_Esp32-master/Grbl_Esp32/nuts_bolts.cpp new file mode 100644 index 0000000..70277d0 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/nuts_bolts.cpp @@ -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 . +*/ + +#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 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); +} + + diff --git a/Grbl_Esp32-master/Grbl_Esp32/nuts_bolts.h b/Grbl_Esp32-master/Grbl_Esp32/nuts_bolts.h new file mode 100644 index 0000000..fe5edbb --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/nuts_bolts.h @@ -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 . +*/ + +#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 void swap ( T& a, T& b ) +{ + T c(a); a=b; b=c; +} + +#endif diff --git a/Grbl_Esp32-master/Grbl_Esp32/planner.cpp b/Grbl_Esp32-master/Grbl_Esp32/planner.cpp new file mode 100644 index 0000000..82ebb43 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/planner.cpp @@ -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 . +*/ + +#include "grbl.h" +#include // 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; idxsteps[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 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= 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(); +} diff --git a/Grbl_Esp32-master/Grbl_Esp32/planner.h b/Grbl_Esp32-master/Grbl_Esp32/planner.h new file mode 100644 index 0000000..c411f24 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/planner.h @@ -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 . +*/ + +#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 \ No newline at end of file diff --git a/Grbl_Esp32-master/Grbl_Esp32/polar_coaster.cpp b/Grbl_Esp32-master/Grbl_Esp32/polar_coaster.cpp new file mode 100644 index 0000000..b90bc89 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/polar_coaster.cpp @@ -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 . + + 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 + diff --git a/Grbl_Esp32-master/Grbl_Esp32/polar_coaster.h b/Grbl_Esp32-master/Grbl_Esp32/polar_coaster.h new file mode 100644 index 0000000..a782617 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/polar_coaster.h @@ -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 . +*/ + +#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<. +*/ + +#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(" "); +// } diff --git a/Grbl_Esp32-master/Grbl_Esp32/print.h b/Grbl_Esp32-master/Grbl_Esp32/print.h new file mode 100644 index 0000000..9ee2559 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/print.h @@ -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 . +*/ + +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/probe.cpp b/Grbl_Esp32-master/Grbl_Esp32/probe.cpp new file mode 100644 index 0000000..4c5093f --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/probe.cpp @@ -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 . +*/ + +#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); + } +} diff --git a/Grbl_Esp32-master/Grbl_Esp32/probe.h b/Grbl_Esp32-master/Grbl_Esp32/probe.h new file mode 100644 index 0000000..b5a3665 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/probe.h @@ -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 . +*/ + +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/protocol.cpp b/Grbl_Esp32-master/Grbl_Esp32/protocol.cpp new file mode 100644 index 0000000..d8037b2 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/protocol.cpp @@ -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 . +*/ + +#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]pwd= + // 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(); + + } +} + diff --git a/Grbl_Esp32-master/Grbl_Esp32/protocol.h b/Grbl_Esp32-master/Grbl_Esp32/protocol.h new file mode 100644 index 0000000..d32b23d --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/protocol.h @@ -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 . +*/ + +#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 + diff --git a/Grbl_Esp32-master/Grbl_Esp32/report.cpp b/Grbl_Esp32-master/Grbl_Esp32/report.cpp new file mode 100644 index 0000000..73a85b5 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/report.cpp @@ -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 . +*/ + +/* + 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= 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); + } +} \ No newline at end of file diff --git a/Grbl_Esp32-master/Grbl_Esp32/report.h b/Grbl_Esp32-master/Grbl_Esp32/report.h new file mode 100644 index 0000000..55f2a42 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/report.h @@ -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 . +*/ + +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/serial.cpp b/Grbl_Esp32-master/Grbl_Esp32/serial.cpp new file mode 100644 index 0000000..5a00714 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/serial.cpp @@ -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 . +*/ + +#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; + } +} + diff --git a/Grbl_Esp32-master/Grbl_Esp32/serial.h b/Grbl_Esp32-master/Grbl_Esp32/serial.h new file mode 100644 index 0000000..3d88a8a --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/serial.h @@ -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 . +*/ + +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/serial2socket.cpp b/Grbl_Esp32-master/Grbl_Esp32/serial2socket.cpp new file mode 100644 index 0000000..fcc74e1 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/serial2socket.cpp @@ -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 +#include +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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/serial2socket.h b/Grbl_Esp32-master/Grbl_Esp32/serial2socket.h new file mode 100644 index 0000000..30a2cb0 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/serial2socket.h @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/servo_axis.cpp b/Grbl_Esp32-master/Grbl_Esp32/servo_axis.cpp new file mode 100644 index 0000000..3759180 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/servo_axis.cpp @@ -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 . + + 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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/servo_axis.h b/Grbl_Esp32-master/Grbl_Esp32/servo_axis.h new file mode 100644 index 0000000..802ab93 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/servo_axis.h @@ -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 . + + 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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/servo_pen.cpp b/Grbl_Esp32-master/Grbl_Esp32/servo_pen.cpp new file mode 100644 index 0000000..cc45dae --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/servo_pen.cpp @@ -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 . + +*/ +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/servo_pen.h b/Grbl_Esp32-master/Grbl_Esp32/servo_pen.h new file mode 100644 index 0000000..ab58bbc --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/servo_pen.h @@ -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 . + + 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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/settings.cpp b/Grbl_Esp32-master/Grbl_Esp32/settings.cpp new file mode 100644 index 0000000..5be2f57 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/settings.cpp @@ -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 . +*/ + +#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 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<. +*/ + +#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 + diff --git a/Grbl_Esp32-master/Grbl_Esp32/solenoid_pen.cpp b/Grbl_Esp32-master/Grbl_Esp32/solenoid_pen.cpp new file mode 100644 index 0000000..2c75bec --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/solenoid_pen.cpp @@ -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 . + +*/ +#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 + diff --git a/Grbl_Esp32-master/Grbl_Esp32/solenoid_pen.h b/Grbl_Esp32-master/Grbl_Esp32/solenoid_pen.h new file mode 100644 index 0000000..e6495a3 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/solenoid_pen.h @@ -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 . + + 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 \ No newline at end of file diff --git a/Grbl_Esp32-master/Grbl_Esp32/spindle_control.cpp b/Grbl_Esp32-master/Grbl_Esp32/spindle_control.cpp new file mode 100644 index 0000000..8877bf3 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/spindle_control.cpp @@ -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 . +*/ + +#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<= 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; +} + + + diff --git a/Grbl_Esp32-master/Grbl_Esp32/spindle_control.h b/Grbl_Esp32-master/Grbl_Esp32/spindle_control.h new file mode 100644 index 0000000..fd4147d --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/spindle_control.h @@ -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 . +*/ + +#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<. +*/ + +#include "grbl.h" + +// Stores the planner block Bresenham algorithm execution data for the segments in the segment +// buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will +// never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1). +// NOTE: This data is copied from the prepped planner blocks so that the planner blocks may be +// discarded when entirely consumed and completed by the segment buffer. Also, AMASS alters this +// data for its own use. +typedef struct { + uint32_t steps[N_AXIS]; + uint32_t step_event_count; + uint8_t direction_bits; +#ifdef VARIABLE_SPINDLE + uint8_t is_pwm_rate_adjusted; // Tracks motions that require constant laser power/rate +#endif +} st_block_t; +static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1]; + +// Primary stepper segment ring buffer. Contains small, short line segments for the stepper +// algorithm to execute, which are "checked-out" incrementally from the first block in the +// planner buffer. Once "checked-out", the steps in the segments buffer cannot be modified by +// the planner, where the remaining planner block steps still can. +typedef struct { + uint16_t n_step; // Number of step events to be executed for this segment + uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate. + uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment. +#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + uint8_t amass_level; // Indicates AMASS level for the ISR to execute this segment +#else + uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing. +#endif +#ifdef VARIABLE_SPINDLE + uint16_t spindle_pwm; +#endif +} segment_t; +static segment_t segment_buffer[SEGMENT_BUFFER_SIZE]; + +// Stepper ISR data struct. Contains the running data for the main stepper ISR. +typedef struct { + // Used by the bresenham line algorithm + uint32_t counter_x, // Counter variables for the bresenham line tracer + counter_y, + counter_z + #if (N_AXIS > A_AXIS) + , counter_a + #endif + #if (N_AXIS > B_AXIS) + , counter_b + #endif + #if (N_AXIS > C_AXIS) + , counter_c + #endif + ; +#ifdef STEP_PULSE_DELAY + uint8_t step_bits; // Stores out_bits output to complete the step pulse delay +#endif + + uint8_t execute_step; // Flags step execution for each interrupt. + uint8_t step_pulse_time; // Step pulse reset time after step rise + uint8_t step_outbits; // The next stepping-bits to be output + uint8_t dir_outbits; +#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + uint32_t steps[N_AXIS]; +#endif + + uint16_t step_count; // Steps remaining in line segment motion + uint8_t exec_block_index; // Tracks the current st_block index. Change indicates new block. + st_block_t *exec_block; // Pointer to the block data for the segment being executed + segment_t *exec_segment; // Pointer to the segment being executed +} stepper_t; +static stepper_t st; + +// Step segment ring buffer indices +static volatile uint8_t segment_buffer_tail; +static uint8_t segment_buffer_head; +static uint8_t segment_next_head; + +// Step and direction port invert masks. +static uint8_t step_port_invert_mask; +static uint8_t dir_port_invert_mask; + +// Used to avoid ISR nesting of the "Stepper Driver Interrupt". Should never occur though. +static volatile uint8_t busy; + +// Pointers for the step segment being prepped from the planner buffer. Accessed only by the +// main program. Pointers may be planning segments or planner blocks ahead of what being executed. +static plan_block_t *pl_block; // Pointer to the planner block being prepped +static st_block_t *st_prep_block; // Pointer to the stepper block data being prepped + +// esp32 work around for diable in main loop +uint64_t stepper_idle_counter; // used to count down until time to disable stepper drivers +bool stepper_idle; + +// Segment preparation data struct. Contains all the necessary information to compute new segments +// based on the current executing planner block. +typedef struct { + uint8_t st_block_index; // Index of stepper common data block being prepped + uint8_t recalculate_flag; + + float dt_remainder; + float steps_remaining; + float step_per_mm; + float req_mm_increment; + +#ifdef PARKING_ENABLE + uint8_t last_st_block_index; + float last_steps_remaining; + float last_step_per_mm; + float last_dt_remainder; +#endif + + uint8_t ramp_type; // Current segment ramp state + float mm_complete; // End of velocity profile from end of current planner block in (mm). + // NOTE: This value must coincide with a step(no mantissa) when converted. + float current_speed; // Current speed at the end of the segment buffer (mm/min) + float maximum_speed; // Maximum speed of executing block. Not always nominal speed. (mm/min) + float exit_speed; // Exit speed of executing block (mm/min) + float accelerate_until; // Acceleration ramp end measured from end of block (mm) + float decelerate_after; // Deceleration ramp start measured from end of block (mm) + +#ifdef VARIABLE_SPINDLE + float inv_rate; // Used by PWM laser mode to speed up segment calculations. + uint16_t current_spindle_pwm; +#endif +} st_prep_t; +static st_prep_t prep; + + +/* "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. Grbl employs + the venerable Bresenham line algorithm to manage and exactly synchronize multi-axis moves. + Unlike the popular DDA algorithm, the Bresenham algorithm is not susceptible to numerical + round-off errors and only requires fast integer counters, meaning low computational overhead + and maximizing the Arduino's capabilities. However, the downside of the Bresenham algorithm + is, for certain multi-axis motions, the non-dominant axes may suffer from un-smooth step + pulse trains, or aliasing, which can lead to strange audible noises or shaking. This is + particularly noticeable or may cause motion issues at low step frequencies (0-5kHz), but + is usually not a physical problem at higher frequencies, although audible. + To improve Bresenham multi-axis performance, Grbl uses what we call an Adaptive Multi-Axis + Step Smoothing (AMASS) algorithm, which does what the name implies. At lower step frequencies, + AMASS artificially increases the Bresenham resolution without effecting the algorithm's + innate exactness. AMASS adapts its resolution levels automatically depending on the step + frequency to be executed, meaning that for even lower step frequencies the step smoothing + level increases. Algorithmically, AMASS is acheived by a simple bit-shifting of the Bresenham + step count for each AMASS level. For example, for a Level 1 step smoothing, we bit shift + the Bresenham step event count, effectively multiplying it by 2, while the axis step counts + remain the same, and then double the stepper ISR frequency. In effect, we are allowing the + non-dominant Bresenham axes step in the intermediate ISR tick, while the dominant axis is + stepping every two ISR ticks, rather than every ISR tick in the traditional sense. At AMASS + Level 2, we simply bit-shift again, so the non-dominant Bresenham axes can step within any + of the four ISR ticks, the dominant axis steps every four ISR ticks, and quadruple the + stepper ISR frequency. And so on. This, in effect, virtually eliminates multi-axis aliasing + issues with the Bresenham algorithm and does not significantly alter Grbl's performance, but + in fact, more efficiently utilizes unused CPU cycles overall throughout all configurations. + AMASS retains the Bresenham algorithm exactness by requiring that it always executes a full + Bresenham step, regardless of AMASS Level. Meaning that for an AMASS Level 2, all four + intermediate steps must be completed such that baseline Bresenham (Level 0) count is always + retained. Similarly, AMASS Level 3 means all eight intermediate steps must be executed. + Although the AMASS Levels are in reality arbitrary, where the baseline Bresenham counts can + be multiplied by any integer value, multiplication by powers of two are simply used to ease + CPU overhead with bitshift integer operations. + This interrupt is simple and dumb by design. All the computational heavy-lifting, as in + determining accelerations, is performed elsewhere. This interrupt pops pre-computed segments, + defined as constant velocity over n number of steps, from the step segment buffer and then + executes them by pulsing the stepper pins appropriately via the Bresenham algorithm. This + ISR is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port + after each pulse. The bresenham line tracer algorithm controls all stepper outputs + simultaneously with these two interrupts. + + NOTE: This interrupt must be as efficient as possible and complete before the next ISR tick, + which for ESP32 Grbl must be less than xx.xusec (TBD). Oscilloscope measured time in + ISR is 5usec typical and 25usec maximum, well below requirement. + NOTE: This ISR expects at least one step to be executed per segment. + + The complete step timing should look this... + Direction pin is set + An optional (via STEP_PULSE_DELAY in config.h) is put after this + The step pin is started + A pulse length is determine (via option $0 ... settings.pulse_microseconds) + The pulse is ended + Direction will remain the same until another step occurs with a change in direction. + + +*/ +#ifdef USE_RMT_STEPS + inline IRAM_ATTR static void stepperRMT_Outputs(); +#endif +// TODO: Replace direct updating of the int32 position counters in the ISR somehow. Perhaps use smaller +// int8 variables and update position counters only when a segment completes. This can get complicated +// with probing and homing cycles that require true real-time positions. +void IRAM_ATTR onStepperDriverTimer(void *para) // ISR It is time to take a step ======================================================================================= +{ + #ifndef USE_RMT_STEPS + uint64_t step_pulse_off_time; + #endif + //const int timer_idx = (int)para; // get the timer index + + TIMERG0.int_clr_timers.t0 = 1; + + if (busy) { + return; // The busy-flag is used to avoid reentering this interrupt + } + + set_direction_pins_on(st.dir_outbits); + + #ifdef USE_RMT_STEPS + stepperRMT_Outputs(); + #else + set_stepper_pins_on(st.step_outbits); + step_pulse_off_time = esp_timer_get_time() + (settings.pulse_microseconds); // determine when to turn off pulse + #endif + + #ifdef USE_UNIPOLAR + unipolar_step(st.step_outbits, st.dir_outbits); + #endif + + busy = true; + // If there is no step segment, attempt to pop one from the stepper buffer + if (st.exec_segment == NULL) { + // Anything in the buffer? If so, load and initialize next step segment. + if (segment_buffer_head != segment_buffer_tail) { + // Initialize new step segment and load number of steps to execute + st.exec_segment = &segment_buffer[segment_buffer_tail]; + + // Initialize step segment timing per step and load number of steps to execute. + Stepper_Timer_WritePeriod(st.exec_segment->cycles_per_tick); + + st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow. + // If the new segment starts a new planner block, initialize stepper variables and counters. + // NOTE: When the segment data index changes, this indicates a new planner block. + if ( st.exec_block_index != st.exec_segment->st_block_index ) { + st.exec_block_index = st.exec_segment->st_block_index; + st.exec_block = &st_block_buffer[st.exec_block_index]; + + // Initialize Bresenham line and distance counters + st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1); + // TODO ABC + } + st.dir_outbits = st.exec_block->direction_bits ^ settings.dir_invert_mask; + +#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + // With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level. + st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.exec_segment->amass_level; + st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level; + st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level; + + #if (N_AXIS > A_AXIS) + st.steps[A_AXIS] = st.exec_block->steps[A_AXIS] >> st.exec_segment->amass_level; + #endif + #if (N_AXIS > B_AXIS) + st.steps[B_AXIS] = st.exec_block->steps[B_AXIS] >> st.exec_segment->amass_level; + #endif + #if (N_AXIS > C_AXIS) + st.steps[C_AXIS] = st.exec_block->steps[C_AXIS] >> st.exec_segment->amass_level; + #endif + +#endif + +#ifdef VARIABLE_SPINDLE + // Set real-time spindle output as segment is loaded, just prior to the first step. + spindle_set_speed(st.exec_segment->spindle_pwm); +#endif + + } else { + // Segment buffer empty. Shutdown. + st_go_idle(); +#if ( (defined VARIABLE_SPINDLE) && (defined SPINDLE_PWM_PIN) ) + if (!(sys.state & STATE_JOG)) { // added to prevent ... jog after probing crash + // Ensure pwm is set properly upon completion of rate-controlled motion. + if (st.exec_block->is_pwm_rate_adjusted) { + spindle_set_speed(settings.spindle_pwm_off_value); + } + } + +#endif + system_set_exec_state_flag(EXEC_CYCLE_STOP); // Flag main program for cycle end + return; // Nothing to do but exit. + } + } + + + // Check probing state. + if (sys_probe_state == PROBE_ACTIVE) { + probe_state_monitor(); + } + + // Reset step out bits. + st.step_outbits = 0; + + // Execute step displacement profile by Bresenham line algorithm +#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + st.counter_x += st.steps[X_AXIS]; +#else + st.counter_x += st.exec_block->steps[X_AXIS]; +#endif + if (st.counter_x > st.exec_block->step_event_count) { + st.step_outbits |= (1<step_event_count; + if (st.exec_block->direction_bits & (1<steps[Y_AXIS]; +#endif + if (st.counter_y > st.exec_block->step_event_count) { + st.step_outbits |= (1<step_event_count; + if (st.exec_block->direction_bits & (1<steps[Z_AXIS]; +#endif + if (st.counter_z > st.exec_block->step_event_count) { + st.step_outbits |= (1<step_event_count; + if (st.exec_block->direction_bits & (1< A_AXIS) + #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + st.counter_a += st.steps[A_AXIS]; + #else + st.counter_a += st.exec_block->steps[A_AXIS]; + #endif + if (st.counter_a > st.exec_block->step_event_count) { + st.step_outbits |= (1<step_event_count; + if (st.exec_block->direction_bits & (1< B_AXIS) + #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + st.counter_b += st.steps[B_AXIS]; + #else + st.counter_b += st.exec_block->steps[B_AXIS]; + #endif + if (st.counter_b > st.exec_block->step_event_count) { + st.step_outbits |= (1<step_event_count; + if (st.exec_block->direction_bits & (1< C_AXIS) + #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + st.counter_c += st.steps[C_AXIS]; + #else + st.counter_c += st.exec_block->steps[C_AXIS]; + #endif + if (st.counter_c > st.exec_block->step_event_count) { + st.step_outbits |= (1<step_event_count; + if (st.exec_block->direction_bits & (1<> 3); +#endif + + // Enable Stepper Driver Interrupt + Stepper_Timer_Start(); +} + +// Reset and clear stepper subsystem variables +void st_reset() +{ + +#ifdef ESP_DEBUG + //Serial.println("st_reset()"); +#endif + // Initialize stepper driver idle state. + st_go_idle(); + + // Initialize stepper algorithm variables. + memset(&prep, 0, sizeof(st_prep_t)); + memset(&st, 0, sizeof(stepper_t)); + st.exec_segment = NULL; + pl_block = NULL; // Planner block pointer used by segment buffer + segment_buffer_tail = 0; + segment_buffer_head = 0; // empty = tail + segment_next_head = 1; + busy = false; + + st_generate_step_dir_invert_masks(); + st.dir_outbits = dir_port_invert_mask; // Initialize direction bits to default. + + // TODO do we need to turn step pins off? + +} + + + + + +void set_direction_pins_on(uint8_t onMask) +{ + // inverts are applied in step generation +#ifdef X_DIRECTION_PIN + digitalWrite(X_DIRECTION_PIN, (onMask & (1<entry_speed_sqr = prep.current_speed*prep.current_speed; // Update entry speed. + pl_block = NULL; // Flag st_prep_segment() to load and check active velocity profile. + } +} + +#ifdef PARKING_ENABLE +// Changes the run state of the step segment buffer to execute the special parking motion. +void st_parking_setup_buffer() +{ + // Store step execution data of partially completed block, if necessary. + if (prep.recalculate_flag & PREP_FLAG_HOLD_PARTIAL_BLOCK) { + prep.last_st_block_index = prep.st_block_index; + prep.last_steps_remaining = prep.steps_remaining; + prep.last_dt_remainder = prep.dt_remainder; + prep.last_step_per_mm = prep.step_per_mm; + } + // Set flags to execute a parking motion + prep.recalculate_flag |= PREP_FLAG_PARKING; + prep.recalculate_flag &= ~(PREP_FLAG_RECALCULATE); + pl_block = NULL; // Always reset parking motion to reload new block. +} + + +// Restores the step segment buffer to the normal run state after a parking motion. +void st_parking_restore_buffer() +{ + // Restore step execution data and flags of partially completed block, if necessary. + if (prep.recalculate_flag & PREP_FLAG_HOLD_PARTIAL_BLOCK) { + st_prep_block = &st_block_buffer[prep.last_st_block_index]; + prep.st_block_index = prep.last_st_block_index; + prep.steps_remaining = prep.last_steps_remaining; + prep.dt_remainder = prep.last_dt_remainder; + prep.step_per_mm = prep.last_step_per_mm; + prep.recalculate_flag = (PREP_FLAG_HOLD_PARTIAL_BLOCK | PREP_FLAG_RECALCULATE); + prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR/prep.step_per_mm; // Recompute this value. + } else { + prep.recalculate_flag = false; + } + pl_block = NULL; // Set to reload next block. +} +#endif + +// Generates the step and direction port invert masks used in the Stepper Interrupt Driver. +void st_generate_step_dir_invert_masks() +{ + /* + uint8_t idx; + step_port_invert_mask = 0; + dir_port_invert_mask = 0; + for (idx=0; idxdirection_bits = pl_block->direction_bits; + uint8_t idx; +#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + for (idx=0; idxsteps[idx] = pl_block->steps[idx]; + } + st_prep_block->step_event_count = pl_block->step_event_count; +#else + // With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS + // level, such that we never divide beyond the original data anywhere in the algorithm. + // If the original data is divided, we can lose a step from integer roundoff. + for (idx=0; idxsteps[idx] = pl_block->steps[idx] << MAX_AMASS_LEVEL; + } + st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL; +#endif + + // Initialize segment buffer data for generating the segments. + prep.steps_remaining = (float)pl_block->step_event_count; + prep.step_per_mm = prep.steps_remaining/pl_block->millimeters; + prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR/prep.step_per_mm; + prep.dt_remainder = 0.0; // Reset for new segment block + + if ((sys.step_control & STEP_CONTROL_EXECUTE_HOLD) || (prep.recalculate_flag & PREP_FLAG_DECEL_OVERRIDE)) { + // New block loaded mid-hold. Override planner block entry speed to enforce deceleration. + prep.current_speed = prep.exit_speed; + pl_block->entry_speed_sqr = prep.exit_speed*prep.exit_speed; + prep.recalculate_flag &= ~(PREP_FLAG_DECEL_OVERRIDE); + } else { + prep.current_speed = sqrt(pl_block->entry_speed_sqr); + } + +#ifdef VARIABLE_SPINDLE + // Setup laser mode variables. PWM rate adjusted motions will always complete a motion with the + // spindle off. + st_prep_block->is_pwm_rate_adjusted = false; + if (settings.flags & BITFLAG_LASER_MODE) { + if (pl_block->condition & PL_COND_FLAG_SPINDLE_CCW) { + // Pre-compute inverse programmed rate to speed up PWM updating per step segment. + prep.inv_rate = 1.0/pl_block->programmed_rate; + st_prep_block->is_pwm_rate_adjusted = true; + } + } +#endif + } + + /* --------------------------------------------------------------------------------- + Compute the velocity profile of a new planner block based on its entry and exit + speeds, or recompute the profile of a partially-completed planner block if the + planner has updated it. For a commanded forced-deceleration, such as from a feed + hold, override the planner velocities and decelerate to the target exit speed. + */ + prep.mm_complete = 0.0; // Default velocity profile complete at 0.0mm from end of block. + float inv_2_accel = 0.5/pl_block->acceleration; + if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { // [Forced Deceleration to Zero Velocity] + // Compute velocity profile parameters for a feed hold in-progress. This profile overrides + // the planner block profile, enforcing a deceleration to zero speed. + prep.ramp_type = RAMP_DECEL; + // Compute decelerate distance relative to end of block. + float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr; + if (decel_dist < 0.0) { + // Deceleration through entire planner block. End of feed hold is not in this block. + prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters); + } else { + prep.mm_complete = decel_dist; // End of feed hold. + prep.exit_speed = 0.0; + } + } else { // [Normal Operation] + // Compute or recompute velocity profile parameters of the prepped planner block. + prep.ramp_type = RAMP_ACCEL; // Initialize as acceleration ramp. + prep.accelerate_until = pl_block->millimeters; + + float exit_speed_sqr; + float nominal_speed; + if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) { + prep.exit_speed = exit_speed_sqr = 0.0; // Enforce stop at end of system motion. + } else { + exit_speed_sqr = plan_get_exec_block_exit_speed_sqr(); + prep.exit_speed = sqrt(exit_speed_sqr); + } + + nominal_speed = plan_compute_profile_nominal_speed(pl_block); + float nominal_speed_sqr = nominal_speed*nominal_speed; + float intersect_distance = + 0.5*(pl_block->millimeters+inv_2_accel*(pl_block->entry_speed_sqr-exit_speed_sqr)); + + if (pl_block->entry_speed_sqr > nominal_speed_sqr) { // Only occurs during override reductions. + prep.accelerate_until = pl_block->millimeters - inv_2_accel*(pl_block->entry_speed_sqr-nominal_speed_sqr); + if (prep.accelerate_until <= 0.0) { // Deceleration-only. + prep.ramp_type = RAMP_DECEL; + // prep.decelerate_after = pl_block->millimeters; + // prep.maximum_speed = prep.current_speed; + + // Compute override block exit speed since it doesn't match the planner exit speed. + prep.exit_speed = sqrt(pl_block->entry_speed_sqr - 2*pl_block->acceleration*pl_block->millimeters); + prep.recalculate_flag |= PREP_FLAG_DECEL_OVERRIDE; // Flag to load next block as deceleration override. + + // TODO: Determine correct handling of parameters in deceleration-only. + // Can be tricky since entry speed will be current speed, as in feed holds. + // Also, look into near-zero speed handling issues with this. + + } else { + // Decelerate to cruise or cruise-decelerate types. Guaranteed to intersect updated plan. + prep.decelerate_after = inv_2_accel*(nominal_speed_sqr-exit_speed_sqr); + prep.maximum_speed = nominal_speed; + prep.ramp_type = RAMP_DECEL_OVERRIDE; + } + } else if (intersect_distance > 0.0) { + if (intersect_distance < pl_block->millimeters) { // Either trapezoid or triangle types + // NOTE: For acceleration-cruise and cruise-only types, following calculation will be 0.0. + prep.decelerate_after = inv_2_accel*(nominal_speed_sqr-exit_speed_sqr); + if (prep.decelerate_after < intersect_distance) { // Trapezoid type + prep.maximum_speed = nominal_speed; + if (pl_block->entry_speed_sqr == nominal_speed_sqr) { + // Cruise-deceleration or cruise-only type. + prep.ramp_type = RAMP_CRUISE; + } else { + // Full-trapezoid or acceleration-cruise types + prep.accelerate_until -= inv_2_accel*(nominal_speed_sqr-pl_block->entry_speed_sqr); + } + } else { // Triangle type + prep.accelerate_until = intersect_distance; + prep.decelerate_after = intersect_distance; + prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr); + } + } else { // Deceleration-only type + prep.ramp_type = RAMP_DECEL; + // prep.decelerate_after = pl_block->millimeters; + // prep.maximum_speed = prep.current_speed; + } + } else { // Acceleration-only type + prep.accelerate_until = 0.0; + // prep.decelerate_after = 0.0; + prep.maximum_speed = prep.exit_speed; + } + } + +#ifdef VARIABLE_SPINDLE + bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); // Force update whenever updating block. +#endif + } + + // Initialize new segment + segment_t *prep_segment = &segment_buffer[segment_buffer_head]; + + // Set new segment to point to the current segment data block. + prep_segment->st_block_index = prep.st_block_index; + + /*------------------------------------------------------------------------------------ + Compute the average velocity of this new segment by determining the total distance + traveled over the segment time DT_SEGMENT. The following code first attempts to create + a full segment based on the current ramp conditions. If the segment time is incomplete + when terminating at a ramp state change, the code will continue to loop through the + progressing ramp states to fill the remaining segment execution time. However, if + an incomplete segment terminates at the end of the velocity profile, the segment is + considered completed despite having a truncated execution time less than DT_SEGMENT. + The velocity profile is always assumed to progress through the ramp sequence: + acceleration ramp, cruising state, and deceleration ramp. Each ramp's travel distance + may range from zero to the length of the block. Velocity profiles can end either at + the end of planner block (typical) or mid-block at the end of a forced deceleration, + such as from a feed hold. + */ + float dt_max = DT_SEGMENT; // Maximum segment time + float dt = 0.0; // Initialize segment time + float time_var = dt_max; // Time worker variable + float mm_var; // mm-Distance worker variable + float speed_var; // Speed worker variable + float mm_remaining = pl_block->millimeters; // New segment distance from end of block. + float minimum_mm = mm_remaining-prep.req_mm_increment; // Guarantee at least one step. + if (minimum_mm < 0.0) { + minimum_mm = 0.0; + } + + do { + switch (prep.ramp_type) { + case RAMP_DECEL_OVERRIDE: + speed_var = pl_block->acceleration*time_var; + mm_var = time_var*(prep.current_speed - 0.5*speed_var); + mm_remaining -= mm_var; + if ((mm_remaining < prep.accelerate_until) || (mm_var <= 0)) { + // Cruise or cruise-deceleration types only for deceleration override. + mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB + time_var = 2.0*(pl_block->millimeters-mm_remaining)/(prep.current_speed+prep.maximum_speed); + prep.ramp_type = RAMP_CRUISE; + prep.current_speed = prep.maximum_speed; + } else { // Mid-deceleration override ramp. + prep.current_speed -= speed_var; + } + break; + case RAMP_ACCEL: + // NOTE: Acceleration ramp only computes during first do-while loop. + speed_var = pl_block->acceleration*time_var; + mm_remaining -= time_var*(prep.current_speed + 0.5*speed_var); + if (mm_remaining < prep.accelerate_until) { // End of acceleration ramp. + // Acceleration-cruise, acceleration-deceleration ramp junction, or end of block. + mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB + time_var = 2.0*(pl_block->millimeters-mm_remaining)/(prep.current_speed+prep.maximum_speed); + if (mm_remaining == prep.decelerate_after) { + prep.ramp_type = RAMP_DECEL; + } else { + prep.ramp_type = RAMP_CRUISE; + } + prep.current_speed = prep.maximum_speed; + } else { // Acceleration only. + prep.current_speed += speed_var; + } + break; + case RAMP_CRUISE: + // NOTE: mm_var used to retain the last mm_remaining for incomplete segment time_var calculations. + // NOTE: If maximum_speed*time_var value is too low, round-off can cause mm_var to not change. To + // prevent this, simply enforce a minimum speed threshold in the planner. + mm_var = mm_remaining - prep.maximum_speed*time_var; + if (mm_var < prep.decelerate_after) { // End of cruise. + // Cruise-deceleration junction or end of block. + time_var = (mm_remaining - prep.decelerate_after)/prep.maximum_speed; + mm_remaining = prep.decelerate_after; // NOTE: 0.0 at EOB + prep.ramp_type = RAMP_DECEL; + } else { // Cruising only. + mm_remaining = mm_var; + } + break; + default: // case RAMP_DECEL: + // NOTE: mm_var used as a misc worker variable to prevent errors when near zero speed. + speed_var = pl_block->acceleration*time_var; // Used as delta speed (mm/min) + if (prep.current_speed > speed_var) { // Check if at or below zero speed. + // Compute distance from end of segment to end of block. + mm_var = mm_remaining - time_var*(prep.current_speed - 0.5*speed_var); // (mm) + if (mm_var > prep.mm_complete) { // Typical case. In deceleration ramp. + mm_remaining = mm_var; + prep.current_speed -= speed_var; + break; // Segment complete. Exit switch-case statement. Continue do-while loop. + } + } + // Otherwise, at end of block or end of forced-deceleration. + time_var = 2.0*(mm_remaining-prep.mm_complete)/(prep.current_speed+prep.exit_speed); + mm_remaining = prep.mm_complete; + prep.current_speed = prep.exit_speed; + } + dt += time_var; // Add computed ramp time to total segment time. + if (dt < dt_max) { + time_var = dt_max - dt; // **Incomplete** At ramp junction. + } else { + if (mm_remaining > minimum_mm) { // Check for very slow segments with zero steps. + // Increase segment time to ensure at least one step in segment. Override and loop + // through distance calculations until minimum_mm or mm_complete. + dt_max += DT_SEGMENT; + time_var = dt_max - dt; + } else { + break; // **Complete** Exit loop. Segment execution time maxed. + } + } + } while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete. + +#ifdef VARIABLE_SPINDLE + /* ----------------------------------------------------------------------------------- + Compute spindle speed PWM output for step segment + */ + + if (st_prep_block->is_pwm_rate_adjusted || (sys.step_control & STEP_CONTROL_UPDATE_SPINDLE_PWM)) { + if (pl_block->condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)) { + float rpm = pl_block->spindle_speed; + // NOTE: Feed and rapid overrides are independent of PWM value and do not alter laser power/rate. + if (st_prep_block->is_pwm_rate_adjusted) { + rpm *= (prep.current_speed * prep.inv_rate); + } + // If current_speed is zero, then may need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE) + // but this would be instantaneous only and during a motion. May not matter at all. + prep.current_spindle_pwm = spindle_compute_pwm_value(rpm); + } else { + sys.spindle_speed = 0.0; + #if ( (defined VARIABLE_SPINDLE) && (defined SPINDLE_PWM_PIN) ) + prep.current_spindle_pwm = settings.spindle_pwm_off_value; + #endif + + } + bit_false(sys.step_control,STEP_CONTROL_UPDATE_SPINDLE_PWM); + } + prep_segment->spindle_pwm = prep.current_spindle_pwm; // Reload segment PWM value +#endif + + /* ----------------------------------------------------------------------------------- + Compute segment step rate, steps to execute, and apply necessary rate corrections. + NOTE: Steps are computed by direct scalar conversion of the millimeter distance + remaining in the block, rather than incrementally tallying the steps executed per + segment. This helps in removing floating point round-off issues of several additions. + However, since floats have only 7.2 significant digits, long moves with extremely + high step counts can exceed the precision of floats, which can lead to lost steps. + Fortunately, this scenario is highly unlikely and unrealistic in CNC machines + supported by Grbl (i.e. exceeding 10 meters axis travel at 200 step/mm). + */ + float step_dist_remaining = prep.step_per_mm*mm_remaining; // Convert mm_remaining to steps + float n_steps_remaining = ceil(step_dist_remaining); // Round-up current steps remaining + float last_n_steps_remaining = ceil(prep.steps_remaining); // Round-up last steps remaining + prep_segment->n_step = last_n_steps_remaining-n_steps_remaining; // Compute number of steps to execute. + + // Bail if we are at the end of a feed hold and don't have a step to execute. + if (prep_segment->n_step == 0) { + if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { + // Less than one step to decelerate to zero speed, but already very close. AMASS + // requires full steps to execute. So, just bail. + bit_true(sys.step_control,STEP_CONTROL_END_MOTION); +#ifdef PARKING_ENABLE + if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) { + prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK; + } +#endif + return; // Segment not generated, but current step data still retained. + } + } + + // Compute segment step rate. Since steps are integers and mm distances traveled are not, + // the end of every segment can have a partial step of varying magnitudes that are not + // executed, because the stepper ISR requires whole steps due to the AMASS algorithm. To + // compensate, we track the time to execute the previous segment's partial step and simply + // apply it with the partial step distance to the current segment, so that it minutely + // adjusts the whole segment rate to keep step output exact. These rate adjustments are + // typically very small and do not adversely effect performance, but ensures that Grbl + // outputs the exact acceleration and velocity profiles as computed by the planner. + dt += prep.dt_remainder; // Apply previous segment partial step execute time + float inv_rate = dt/(last_n_steps_remaining - step_dist_remaining); // Compute adjusted step rate inverse + + // Compute CPU cycles per step for the prepped segment. + uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step) + +#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + // Compute step timing and multi-axis smoothing level. + // NOTE: AMASS overdrives the timer with each level, so only one prescalar is required. + if (cycles < AMASS_LEVEL1) { + prep_segment->amass_level = 0; + } else { + if (cycles < AMASS_LEVEL2) { + prep_segment->amass_level = 1; + } else if (cycles < AMASS_LEVEL3) { + prep_segment->amass_level = 2; + } else { + prep_segment->amass_level = 3; + } + cycles >>= prep_segment->amass_level; + prep_segment->n_step <<= prep_segment->amass_level; + } + if (cycles < (1UL << 16)) { + prep_segment->cycles_per_tick = cycles; // < 65536 (4.1ms @ 16MHz) + } else { + prep_segment->cycles_per_tick = 0xffff; // Just set the slowest speed possible. + } +#else + // Compute step timing and timer prescalar for normal step generation. + if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz) + prep_segment->prescaler = 1; // prescaler: 0 + prep_segment->cycles_per_tick = cycles; + } else if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz) + prep_segment->prescaler = 2; // prescaler: 8 + prep_segment->cycles_per_tick = cycles >> 3; + } else { + prep_segment->prescaler = 3; // prescaler: 64 + if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz) + prep_segment->cycles_per_tick = cycles >> 6; + } else { // Just set the slowest speed possible. (Around 4 step/sec.) + prep_segment->cycles_per_tick = 0xffff; + } + } +#endif + + // Segment complete! Increment segment buffer indices, so stepper ISR can immediately execute it. + segment_buffer_head = segment_next_head; + if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { + segment_next_head = 0; + } + + // Update the appropriate planner and segment data. + pl_block->millimeters = mm_remaining; + prep.steps_remaining = n_steps_remaining; + prep.dt_remainder = (n_steps_remaining - step_dist_remaining)*inv_rate; + + // Check for exit conditions and flag to load next planner block. + if (mm_remaining == prep.mm_complete) { + // End of planner block or forced-termination. No more distance to be executed. + if (mm_remaining > 0.0) { // At end of forced-termination. + // Reset prep parameters for resuming and then bail. Allow the stepper ISR to complete + // the segment queue, where realtime protocol will set new state upon receiving the + // cycle stop flag from the ISR. Prep_segment is blocked until then. + bit_true(sys.step_control,STEP_CONTROL_END_MOTION); +#ifdef PARKING_ENABLE + if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) { + prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK; + } +#endif + return; // Bail! + } else { // End of planner block + // The planner block is complete. All steps are set to be executed in the segment buffer. + if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) { + bit_true(sys.step_control,STEP_CONTROL_END_MOTION); + return; + } + pl_block = NULL; // Set pointer to indicate check and load next planner block. + plan_discard_current_block(); + } + } + + } +} + + + +// Called by realtime status reporting to fetch the current speed being executed. This value +// however is not exactly the current speed, but the speed computed in the last step segment +// in the segment buffer. It will always be behind by up to the number of segment blocks (-1) +// divided by the ACCELERATION TICKS PER SECOND in seconds. +float st_get_realtime_rate() +{ + if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) { + return prep.current_speed; + } + return 0.0f; +} + +void IRAM_ATTR Stepper_Timer_WritePeriod(uint64_t alarm_val) +{ + timer_set_alarm_value(STEP_TIMER_GROUP, STEP_TIMER_INDEX, alarm_val); +} + +void IRAM_ATTR Stepper_Timer_Start() +{ +#ifdef ESP_DEBUG + //Serial.println("ST Start"); +#endif + + timer_set_counter_value(STEP_TIMER_GROUP, STEP_TIMER_INDEX, 0x00000000ULL); + + timer_start(STEP_TIMER_GROUP, STEP_TIMER_INDEX); + TIMERG0.hw_timer[STEP_TIMER_INDEX].config.alarm_en = TIMER_ALARM_EN; + +} + +void IRAM_ATTR Stepper_Timer_Stop() +{ +#ifdef ESP_DEBUG + //Serial.println("ST Stop"); +#endif + + timer_pause(STEP_TIMER_GROUP, STEP_TIMER_INDEX); + +} + + +void set_stepper_disable(uint8_t isOn) // isOn = true // to disable +{ + #ifdef TRINAMIC + return; + #endif + + if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { + isOn = !isOn; // Apply pin invert. + } + + #ifdef USE_UNIPOLAR + unipolar_disable(isOn); + #endif + +#ifdef STEPPERS_DISABLE_PIN + digitalWrite(STEPPERS_DISABLE_PIN, isOn ); +#endif +} + +bool get_stepper_disable() // returns true if steppers are disabled +{ + bool disabled = false; + +#ifdef STEPPERS_DISABLE_PIN + disabled = digitalRead(STEPPERS_DISABLE_PIN); +#else + return false; // thery are never disabled if there is no pin defined +#endif + + if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { + disabled = !disabled; // Apply pin invert. + } + + return disabled; + +} + + + diff --git a/Grbl_Esp32-master/Grbl_Esp32/stepper.h b/Grbl_Esp32-master/Grbl_Esp32/stepper.h new file mode 100644 index 0000000..d2dd6bb --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/stepper.h @@ -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 . +*/ + +#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 \ No newline at end of file diff --git a/Grbl_Esp32-master/Grbl_Esp32/system.cpp b/Grbl_Esp32-master/Grbl_Esp32/system.cpp new file mode 100644 index 0000000..fbfd226 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/system.cpp @@ -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 . +*/ + +#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, ¶meter)) { 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 -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<. +*/ + +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/tdef.h b/Grbl_Esp32-master/Grbl_Esp32/tdef.h new file mode 100644 index 0000000..5e73f4c --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/tdef.h @@ -0,0 +1,7 @@ +#ifndef tdef_h +#define tdef_h + +#include "grbl.h" + + +#endif diff --git a/Grbl_Esp32-master/Grbl_Esp32/telnet_server.cpp b/Grbl_Esp32-master/Grbl_Esp32/telnet_server.cpp new file mode 100644 index 0000000..06a99a7 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/telnet_server.cpp @@ -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 +#include +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/telnet_server.h b/Grbl_Esp32-master/Grbl_Esp32/telnet_server.h new file mode 100644 index 0000000..10d5703 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/telnet_server.h @@ -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 + diff --git a/Grbl_Esp32-master/Grbl_Esp32/tests/parsetest.nc b/Grbl_Esp32-master/Grbl_Esp32/tests/parsetest.nc new file mode 100644 index 0000000..2ef7bee --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/tests/parsetest.nc @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/web_server.cpp b/Grbl_Esp32-master/Grbl_Esp32/web_server.cpp new file mode 100644 index 0000000..fdc9be7 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/web_server.cpp @@ -0,0 +1,1889 @@ +/* + web_server.cpp - web 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_HTTP) + +#include "wifiservices.h" + +#include "grbl.h" + +#include "commands.h" +#include "espresponse.h" +#include "serial2socket.h" +#include "web_server.h" +#include +#include "wificonfig.h" +#include +#include +#include +#ifdef ENABLE_SD_CARD +#include +#include "grbl_sd.h" +#endif +#include +#include "report.h" +#include +#include +#include +#include +#include +#ifdef ENABLE_MDNS +#include +#endif +#ifdef ENABLE_SSDP +#include +#endif +#ifdef ENABLE_CAPTIVE_PORTAL +#include +const byte DNS_PORT = 53; +DNSServer dnsServer; +#endif +#include + +//embedded response file if no files on SPIFFS +#include "nofile.h" + +//Upload status +typedef enum { + UPLOAD_STATUS_NONE = 0, + UPLOAD_STATUS_FAILED = 1, + UPLOAD_STATUS_CANCELLED = 2, + UPLOAD_STATUS_SUCCESSFUL = 3, + UPLOAD_STATUS_ONGOING = 4 +} upload_status_type; + + +//Default 404 +const char PAGE_404 [] = "\n\nRedirecting... \n\n\n
Unknown page : $QUERY$- you will be redirected...\n

\nif not redirected, click here\n

\n\n\n\n
\n\n\n\n"; +const char PAGE_CAPTIVE [] = "\n\nCaptive Portal \n\n\n
Captive Portal page : $QUERY$- you will be redirected...\n

\nif not redirected, click here\n

\n\n\n\n
\n\n\n\n"; + +//error codes fo upload +#define ESP_ERROR_AUTHENTICATION 1 +#define ESP_ERROR_FILE_CREATION 2 +#define ESP_ERROR_FILE_WRITE 3 +#define ESP_ERROR_UPLOAD 4 +#define ESP_ERROR_NOT_ENOUGH_SPACE 5 +#define ESP_ERROR_UPLOAD_CANCELLED 6 +#define ESP_ERROR_FILE_CLOSE 7 +#define ESP_ERROR_NO_SD 8 + +Web_Server web_server; +bool Web_Server::_setupdone = false; +uint16_t Web_Server::_port = 0; +long Web_Server::_id_connection = 0; +uint8_t Web_Server::_upload_status = UPLOAD_STATUS_NONE; +WebServer * Web_Server::_webserver = NULL; +WebSocketsServer * Web_Server::_socket_server = NULL; +#ifdef ENABLE_AUTHENTICATION +auth_ip * Web_Server::_head = NULL; +uint8_t Web_Server::_nb_ip = 0; +#define MAX_AUTH_IP 10 +#endif +Web_Server::Web_Server(){ + +} +Web_Server::~Web_Server(){ + end(); +} + +long Web_Server::get_client_ID() { + return _id_connection; +} + +bool Web_Server::begin(){ + + bool no_error = true; + _setupdone = false; + Preferences prefs; + prefs.begin(NAMESPACE, true); + int8_t penabled = prefs.getChar(HTTP_ENABLE_ENTRY, DEFAULT_HTTP_STATE); + //Get http port + _port = prefs.getUShort(HTTP_PORT_ENTRY, DEFAULT_WEBSERVER_PORT); + prefs.end(); + if (penabled == 0) return false; + //create instance + _webserver= new WebServer(_port); +#ifdef ENABLE_AUTHENTICATION + //here the list of headers to be recorded + const char * headerkeys[] = {"Cookie"} ; + size_t headerkeyssize = sizeof (headerkeys) / sizeof (char*); + //ask server to track these headers + _webserver->collectHeaders (headerkeys, headerkeyssize ); +#endif + _socket_server = new WebSocketsServer(_port + 1); + _socket_server->begin(); + _socket_server->onEvent(handle_Websocket_Event); + + + //Websocket output + Serial2Socket.attachWS(_socket_server); + + //events functions + //_web_events->onConnect(handle_onevent_connect); + //events management + // _webserver->addHandler(_web_events); + + //Websocket function + //_web_socket->onEvent(handle_Websocket_Event); + //Websocket management + //_webserver->addHandler(_web_socket); + + //Web server handlers + //trick to catch command line on "/" before file being processed + _webserver->on("/",HTTP_ANY, handle_root); + + //Page not found handler + _webserver->onNotFound (handle_not_found); + + //need to be there even no authentication to say to UI no authentication + _webserver->on("/login", HTTP_ANY, handle_login); + + //web commands + _webserver->on ("/command", HTTP_ANY, handle_web_command); + _webserver->on ("/command_silent", HTTP_ANY, handle_web_command_silent); + + //SPIFFS + _webserver->on ("/files", HTTP_ANY, handleFileList, SPIFFSFileupload); + + //web update + _webserver->on ("/updatefw", HTTP_ANY, handleUpdate, WebUpdateUpload); + +#ifdef ENABLE_SD_CARD + //Direct SD management + _webserver->on("/upload", HTTP_ANY, handle_direct_SDFileList,SDFile_direct_upload); + //_webserver->on("/SD", HTTP_ANY, handle_SDCARD); +#endif + +#ifdef ENABLE_CAPTIVE_PORTAL + if(WiFi.getMode() != WIFI_STA){ + // if DNSServer is started with "*" for domain name, it will reply with + // provided IP to all DNS request + dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); + grbl_send(CLIENT_ALL,"[MSG:Captive Portal Started]\r\n"); + _webserver->on ("/generate_204", HTTP_ANY, handle_root); + _webserver->on ("/gconnectivitycheck.gstatic.com", HTTP_ANY, handle_root); + //do not forget the / at the end + _webserver->on ("/fwlink/", HTTP_ANY, handle_root); + } +#endif + +#ifdef ENABLE_SSDP + //SSDP service presentation + if(WiFi.getMode() == WIFI_STA){ + _webserver->on ("/description.xml", HTTP_GET, handle_SSDP); + //Add specific for SSDP + SSDP.setSchemaURL ("description.xml"); + SSDP.setHTTPPort (_port); + SSDP.setName (wifi_config.Hostname()); + SSDP.setURL ("/"); + SSDP.setDeviceType ("upnp:rootdevice"); + /*Any customization could be here + SSDP.setModelName (ESP32_MODEL_NAME); + SSDP.setModelURL (ESP32_MODEL_URL); + SSDP.setModelNumber (ESP_MODEL_NUMBER); + SSDP.setManufacturer (ESP_MANUFACTURER_NAME); + SSDP.setManufacturerURL (ESP_MANUFACTURER_URL); + */ + + //Start SSDP + grbl_send(CLIENT_ALL,"[MSG:SSDP Started]\r\n"); + SSDP.begin(); + } +#endif + grbl_send(CLIENT_ALL,"[MSG:HTTP Started]\r\n"); + //start webserver + _webserver->begin(); +#ifdef ENABLE_MDNS + //add mDNS + if(WiFi.getMode() == WIFI_STA){ + MDNS.addService("http","tcp",_port); + } +#endif + _setupdone = true; + return no_error; +} + +void Web_Server::end(){ + _setupdone = false; +#ifdef ENABLE_SSDP + SSDP.end(); +#endif //ENABLE_SSDP +#ifdef ENABLE_MDNS + //remove mDNS + mdns_service_remove("_http", "_tcp"); +#endif + if (_socket_server) { + delete _socket_server; + _socket_server = NULL; + } + if (_webserver) { + delete _webserver; + _webserver = NULL; + } +#ifdef ENABLE_AUTHENTICATION + while (_head) { + auth_ip * current = _head; + _head = _head->_next; + delete current; + } + _nb_ip = 0; +#endif +} + +//Root of Webserver///////////////////////////////////////////////////// + +void Web_Server::handle_root() +{ + String path = "/index.html"; + String contentType = getContentType(path); + String pathWithGz = path + ".gz"; + //if have a index.html or gzip version this is default root page + if((SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) && !_webserver->hasArg("forcefallback") && _webserver->arg("forcefallback")!="yes") { + if(SPIFFS.exists(pathWithGz)) { + path = pathWithGz; + } + File file = SPIFFS.open(path, FILE_READ); + _webserver->streamFile(file, contentType); + file.close(); + return; + } + //if no lets launch the default content + _webserver->sendHeader("Content-Encoding", "gzip"); + _webserver->send_P(200,"text/html",PAGE_NOFILES,PAGE_NOFILES_SIZE); +} + +//Handle not registred path on SPIFFS neither SD /////////////////////// +void Web_Server:: handle_not_found() +{ + if (is_authenticated() == LEVEL_GUEST) { + _webserver->sendContent_P("HTTP/1.1 301 OK\r\nLocation: /\r\nCache-Control: no-cache\r\n\r\n"); + //_webserver->client().stop(); + return; + } + bool page_not_found = false; + String path = _webserver->urlDecode(_webserver->uri()); + String contentType = getContentType(path); + String pathWithGz = path + ".gz"; + +#ifdef ENABLE_SD_CARD + if ((path.substring(0,4) == "/SD/")) { + //remove /SD + path = path.substring(3); + if(SD.exists((char *)pathWithGz.c_str()) || SD.exists((char *)path.c_str())) { + if(SD.exists((char *)pathWithGz.c_str())) { + path = pathWithGz; + } + File datafile = SD.open((char *)path.c_str()); + if (datafile) { + vTaskDelay(1 / portTICK_RATE_MS); + size_t totalFileSize = datafile.size(); + size_t i = 0; + bool done = false; + _webserver->setContentLength(totalFileSize); + _webserver->send(200, contentType, ""); + uint8_t buf[1024]; + while (!done){ + vTaskDelay(1 / portTICK_RATE_MS); + int v = datafile.read(buf,1024); + if ((v == -1) || (v == 0)) { + done = true; + } else { + _webserver->client().write(buf,1024); + i+=v; + } + if (i >= totalFileSize) done = true; + } + datafile.close(); + if ( i != totalFileSize) { + //error: TBD + } + return; + } + } + String content = "cannot find "; + content+=path; + _webserver->send(404,"text/plain",content.c_str()); + return; + } else +#endif + if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { + if(SPIFFS.exists(pathWithGz)) { + path = pathWithGz; + } + File file = SPIFFS.open(path, FILE_READ); + _webserver->streamFile(file, contentType); + file.close(); + return; + } else { + page_not_found = true; + } + + if (page_not_found ) { +#ifdef ENABLE_CAPTIVE_PORTAL + if (WiFi.getMode()!=WIFI_STA ) { + String contentType= PAGE_CAPTIVE; + String stmp = WiFi.softAPIP().toString(); + //Web address = ip + port + String KEY_IP = "$WEB_ADDRESS$"; + String KEY_QUERY = "$QUERY$"; + if (_port != 80) { + stmp+=":"; + stmp+=String(_port); + } + contentType.replace(KEY_IP,stmp); + contentType.replace(KEY_IP,stmp); + contentType.replace(KEY_QUERY,_webserver->uri()); + _webserver->send(200,"text/html",contentType); + //_webserver->sendContent_P(NOT_AUTH_NF); + //_webserver->client().stop(); + return; + } +#endif + path = "/404.htm"; + contentType = getContentType(path); + pathWithGz = path + ".gz"; + if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { + if(SPIFFS.exists(pathWithGz)) { + path = pathWithGz; + } + File file = SPIFFS.open(path, FILE_READ); + _webserver->streamFile(file, contentType); + file.close(); + + } else { + //if not template use default page + contentType = PAGE_404; + String stmp; + if (WiFi.getMode()==WIFI_STA ) { + stmp=WiFi.localIP().toString(); + } else { + stmp=WiFi.softAPIP().toString(); + } + //Web address = ip + port + String KEY_IP = "$WEB_ADDRESS$"; + String KEY_QUERY = "$QUERY$"; + if ( _port != 80) { + stmp+=":"; + stmp+=String(_port); + } + contentType.replace(KEY_IP,stmp); + contentType.replace(KEY_QUERY,_webserver->uri()); + _webserver->send(200,"text/html",contentType); + } + } +} + +#ifdef ENABLE_SSDP +//http SSDP xml presentation +void Web_Server::handle_SSDP () +{ + StreamString sschema ; + if (sschema.reserve (1024) ) { + String templ = "" + "" + "" + "1" + "0" + "" + "http://%s:%u/" + "" + "upnp:rootdevice" + "%s" + "/" + "%s" + "ESP32" + "Marlin" + "http://espressif.com/en/products/hardware/esp-wroom-32/overview" + "Espressif Systems" + "http://espressif.com" + "uuid:%s" + "" + "\r\n" + "\r\n"; + char uuid[37]; + String sip = WiFi.localIP().toString(); + uint32_t chipId = (uint16_t) (ESP.getEfuseMac() >> 32); + sprintf (uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x", + (uint16_t) ( (chipId >> 16) & 0xff), + (uint16_t) ( (chipId >> 8) & 0xff), + (uint16_t) chipId & 0xff ); + String serialNumber = String (chipId); + sschema.printf (templ.c_str(), + sip.c_str(), + _port, + wifi_config.Hostname().c_str(), + serialNumber.c_str(), + uuid); + _webserver->send (200, "text/xml", (String) sschema); + } else { + _webserver->send (500); + } +} +#endif + +bool Web_Server::is_realtime_cmd(char c){ + if (c == CMD_STATUS_REPORT) return true; + if (c == CMD_CYCLE_START) return true; + if (c == CMD_RESET) return true; + if (c == CMD_FEED_HOLD) return true; + if (c == CMD_SAFETY_DOOR) return true; + if (c == CMD_JOG_CANCEL) return true; + if (c == CMD_DEBUG_REPORT) return true; + if (c == CMD_FEED_OVR_RESET) return true; + if (c == CMD_FEED_OVR_COARSE_PLUS) return true; + if (c == CMD_FEED_OVR_COARSE_MINUS) return true; + if (c == CMD_FEED_OVR_FINE_PLUS) return true; + if (c == CMD_FEED_OVR_FINE_MINUS) return true; + if (c == CMD_RAPID_OVR_RESET) return true; + if (c == CMD_RAPID_OVR_MEDIUM) return true; + if (c == CMD_RAPID_OVR_LOW) return true; + if (c == CMD_SPINDLE_OVR_COARSE_PLUS) return true; + if (c == CMD_SPINDLE_OVR_COARSE_MINUS) return true; + if (c == CMD_SPINDLE_OVR_FINE_PLUS) return true; + if (c == CMD_SPINDLE_OVR_FINE_MINUS) return true; + if (c == CMD_SPINDLE_OVR_STOP) return true; + if (c == CMD_COOLANT_FLOOD_OVR_TOGGLE) return true; + if (c == CMD_COOLANT_MIST_OVR_TOGGLE) return true; + return false; +} + +//Handle web command query and send answer////////////////////////////// +void Web_Server::handle_web_command () +{ + //to save time if already disconnected + //if (_webserver->hasArg ("PAGEID") ) { + // if (_webserver->arg ("PAGEID").length() > 0 ) { + // if (_webserver->arg ("PAGEID").toInt() != _id_connection) { + // _webserver->send (200, "text/plain", "Invalid command"); + // return; + // } + // } + //} + level_authenticate_type auth_level = is_authenticated(); + String cmd = ""; + if (_webserver->hasArg ("plain") || _webserver->hasArg ("commandText") ) { + if (_webserver->hasArg ("plain") ) { + cmd = _webserver->arg ("plain"); + } else { + cmd = _webserver->arg ("commandText"); + } + } else { + _webserver->send (200, "text/plain", "Invalid command"); + return; + } + //if it is internal command [ESPXXX] + cmd.trim(); + int ESPpos = cmd.indexOf ("[ESP"); + if (ESPpos > -1) { + //is there the second part? + int ESPpos2 = cmd.indexOf ("]", ESPpos); + if (ESPpos2 > -1) { + //Split in command and parameters + String cmd_part1 = cmd.substring (ESPpos + 4, ESPpos2); + String cmd_part2 = ""; + //only [ESP800] is allowed login free if authentication is enabled + if ( (auth_level == LEVEL_GUEST) && (cmd_part1.toInt() != 800) ) { + _webserver->send (401, "text/plain", "Authentication failed!\n"); + return; + } + //is there space for parameters? + if (ESPpos2 < cmd.length() ) { + cmd_part2 = cmd.substring (ESPpos2 + 1); + } + //if command is a valid number then execute command + if (cmd_part1.toInt() != 0) { + ESPResponseStream espresponse(_webserver); + //commmand is web only + COMMANDS::execute_internal_command (cmd_part1.toInt(), cmd_part2, auth_level, &espresponse); + //flush + espresponse.flush(); + } + //if not is not a valid [ESPXXX] command + } + } else { //execute GCODE + if (auth_level == LEVEL_GUEST) { + _webserver->send (401, "text/plain", "Authentication failed!\n"); + return; + } + //Instead of send several commands one by one by web / send full set and split here + String scmd; + String res = ""; + uint8_t sindex = 0; + scmd = get_Splited_Value(cmd,'\n', sindex); + while ( scmd != "" ){ + if ((scmd.length() == 2) && (scmd[0] == 0xC2)){ + scmd[0]=scmd[1]; + scmd.remove(1,1); + } + if (scmd.length() > 1)scmd += "\n"; + else if (!is_realtime_cmd(scmd[0]) )scmd += "\n"; + if (!Serial2Socket.push(scmd.c_str()))res = "Error"; + sindex++; + scmd = get_Splited_Value(cmd,'\n', sindex); + } + _webserver->send (200, "text/plain", res.c_str()); + } +} +//Handle web command query and send answer////////////////////////////// +void Web_Server::handle_web_command_silent () +{ + //to save time if already disconnected + //if (_webserver->hasArg ("PAGEID") ) { + // if (_webserver->arg ("PAGEID").length() > 0 ) { + // if (_webserver->arg ("PAGEID").toInt() != _id_connection) { + // _webserver->send (200, "text/plain", "Invalid command"); + // return; + // } + // } + //} + level_authenticate_type auth_level = is_authenticated(); + String cmd = ""; + if (_webserver->hasArg ("plain") || _webserver->hasArg ("commandText") ) { + if (_webserver->hasArg ("plain") ) { + cmd = _webserver->arg ("plain"); + } else { + cmd = _webserver->arg ("commandText"); + } + } else { + _webserver->send (200, "text/plain", "Invalid command"); + return; + } + //if it is internal command [ESPXXX] + cmd.trim(); + int ESPpos = cmd.indexOf ("[ESP"); + if (ESPpos > -1) { + //is there the second part? + int ESPpos2 = cmd.indexOf ("]", ESPpos); + if (ESPpos2 > -1) { + //Split in command and parameters + String cmd_part1 = cmd.substring (ESPpos + 4, ESPpos2); + String cmd_part2 = ""; + //only [ESP800] is allowed login free if authentication is enabled + if ( (auth_level == LEVEL_GUEST) && (cmd_part1.toInt() != 800) ) { + _webserver->send (401, "text/plain", "Authentication failed!\n"); + return; + } + //is there space for parameters? + if (ESPpos2 < cmd.length() ) { + cmd_part2 = cmd.substring (ESPpos2 + 1); + } + //if command is a valid number then execute command + if (cmd_part1.toInt() != 0) { + //commmand is web only + if(COMMANDS::execute_internal_command (cmd_part1.toInt(), cmd_part2, auth_level, NULL)) _webserver->send (200, "text/plain", "ok"); + else _webserver->send (200, "text/plain", "error"); + } + //if not is not a valid [ESPXXX] command + } + } else { //execute GCODE + if (auth_level == LEVEL_GUEST) { + _webserver->send (401, "text/plain", "Authentication failed!\n"); + return; + } + //Instead of send several commands one by one by web / send full set and split here + String scmd; + uint8_t sindex = 0; + scmd = get_Splited_Value(cmd,'\n', sindex); + String res = ""; + while ( scmd != "" ){ + if (scmd.length() > 1)scmd+="\n"; + else if (!is_realtime_cmd(scmd[0]) )scmd+="\n"; + if (!Serial2Socket.push(scmd.c_str()))res = "Error"; + sindex++; + scmd = get_Splited_Value(cmd,'\n', sindex); + } + _webserver->send (200, "text/plain", res.c_str()); + } +} + + +//login status check +void Web_Server::handle_login() +{ +#ifdef ENABLE_AUTHENTICATION + String smsg; + String sUser,sPassword; + String auths; + int code = 200; + bool msg_alert_error=false; + //disconnect can be done anytime no need to check credential + if (_webserver->hasArg("DISCONNECT")) { + String cookie = _webserver->header("Cookie"); + int pos = cookie.indexOf("ESPSESSIONID="); + String sessionID; + if (pos!= -1) { + int pos2 = cookie.indexOf(";",pos); + sessionID = cookie.substring(pos+strlen("ESPSESSIONID="),pos2); + } + ClearAuthIP(_webserver->client().remoteIP(), sessionID.c_str()); + _webserver->sendHeader("Set-Cookie","ESPSESSIONID=0"); + _webserver->sendHeader("Cache-Control","no-cache"); + String buffer2send = "{\"status\":\"Ok\",\"authentication_lvl\":\"guest\"}"; + _webserver->send(code, "application/json", buffer2send); + //_webserver->client().stop(); + return; + } + + level_authenticate_type auth_level = is_authenticated(); + if (auth_level == LEVEL_GUEST) auths = "guest"; + else if (auth_level == LEVEL_USER) auths = "user"; + else if (auth_level == LEVEL_ADMIN) auths = "admin"; + else auths = "???"; + + //check is it is a submission or a query + if (_webserver->hasArg("SUBMIT")) { + //is there a correct list of query? + if ( _webserver->hasArg("PASSWORD") && _webserver->hasArg("USER")) { + //USER + sUser = _webserver->arg("USER"); + if ( !((sUser == DEFAULT_ADMIN_LOGIN) || (sUser == DEFAULT_USER_LOGIN))) { + msg_alert_error=true; + smsg = "Error : Incorrect User"; + code = 401; + } + if (msg_alert_error == false) { + //Password + sPassword = _webserver->arg("PASSWORD"); + String sadminPassword; + + Preferences prefs; + prefs.begin(NAMESPACE, true); + String defV = DEFAULT_ADMIN_PWD; + sadminPassword = prefs.getString(ADMIN_PWD_ENTRY, defV); + String suserPassword; + defV = DEFAULT_USER_PWD; + suserPassword = prefs.getString(USER_PWD_ENTRY, defV); + prefs.end(); + + if(!(((sUser == DEFAULT_ADMIN_LOGIN) && (strcmp(sPassword.c_str(),sadminPassword.c_str()) == 0)) || + ((sUser == DEFAULT_USER_LOGIN) && (strcmp(sPassword.c_str(),suserPassword.c_str()) == 0)))) { + msg_alert_error=true; + smsg = "Error: Incorrect password"; + code = 401; + } + } + } else { + msg_alert_error=true; + smsg = "Error: Missing data"; + code = 500; + } + //change password + if (_webserver->hasArg("PASSWORD") && _webserver->hasArg("USER") && _webserver->hasArg("NEWPASSWORD") && (msg_alert_error==false) ) { + String newpassword = _webserver->arg("NEWPASSWORD"); + if (COMMANDS::isLocalPasswordValid(newpassword.c_str())) { + String spos; + if(sUser == DEFAULT_ADMIN_LOGIN) spos = ADMIN_PWD_ENTRY; + else spos = USER_PWD_ENTRY; + + Preferences prefs; + prefs.begin(NAMESPACE, false); + if (prefs.putString(spos.c_str(), newpassword) != newpassword.length()) { + msg_alert_error = true; + smsg = "Error: Cannot apply changes"; + code = 500; + } + prefs.end(); + } else { + msg_alert_error=true; + smsg = "Error: Incorrect password"; + code = 500; + } + } + if ((code == 200) || (code == 500)) { + level_authenticate_type current_auth_level; + if(sUser == DEFAULT_ADMIN_LOGIN) { + current_auth_level = LEVEL_ADMIN; + } else if(sUser == DEFAULT_USER_LOGIN){ + current_auth_level = LEVEL_USER; + } else { + current_auth_level = LEVEL_GUEST; + } + //create Session + if ((current_auth_level != auth_level) || (auth_level== LEVEL_GUEST)) { + auth_ip * current_auth = new auth_ip; + current_auth->level = current_auth_level; + current_auth->ip=_webserver->client().remoteIP(); + strcpy(current_auth->sessionID,create_session_ID()); + strcpy(current_auth->userID,sUser.c_str()); + current_auth->last_time=millis(); + if (AddAuthIP(current_auth)) { + String tmps ="ESPSESSIONID="; + tmps+=current_auth->sessionID; + _webserver->sendHeader("Set-Cookie",tmps); + _webserver->sendHeader("Cache-Control","no-cache"); + switch(current_auth->level) { + case LEVEL_ADMIN: + auths = "admin"; + break; + case LEVEL_USER: + auths = "user"; + break; + default: + auths = "guest"; + break; + } + } else { + delete current_auth; + msg_alert_error=true; + code = 500; + smsg = "Error: Too many connections"; + } + } + } + if (code == 200) smsg = "Ok"; + + //build JSON + String buffer2send = "{\"status\":\"" + smsg + "\",\"authentication_lvl\":\""; + buffer2send += auths; + buffer2send += "\"}"; + _webserver->send(code, "application/json", buffer2send); + } else { + if (auth_level != LEVEL_GUEST) { + String cookie = _webserver->header("Cookie"); + int pos = cookie.indexOf("ESPSESSIONID="); + String sessionID; + if (pos!= -1) { + int pos2 = cookie.indexOf(";",pos); + sessionID = cookie.substring(pos+strlen("ESPSESSIONID="),pos2); + auth_ip * current_auth_info = GetAuth(_webserver->client().remoteIP(), sessionID.c_str()); + if (current_auth_info != NULL){ + sUser = current_auth_info->userID; + } + } + } + String buffer2send = "{\"status\":\"200\",\"authentication_lvl\":\""; + buffer2send += auths; + buffer2send += "\",\"user\":\""; + buffer2send += sUser; + buffer2send +="\"}"; + _webserver->send(code, "application/json", buffer2send); + } +#else + _webserver->sendHeader("Cache-Control","no-cache"); + _webserver->send(200, "application/json", "{\"status\":\"Ok\",\"authentication_lvl\":\"admin\"}"); +#endif +} +//SPIFFS +//SPIFFS files list and file commands +void Web_Server::handleFileList () +{ + level_authenticate_type auth_level = is_authenticated(); + if (auth_level == LEVEL_GUEST) { + _upload_status = UPLOAD_STATUS_NONE; + _webserver->send (401, "text/plain", "Authentication failed!\n"); + return; + } + String path ; + String status = "Ok"; + if (_upload_status == UPLOAD_STATUS_FAILED) { + status = "Upload failed"; + _upload_status = UPLOAD_STATUS_NONE; + } + _upload_status = UPLOAD_STATUS_NONE; + //be sure root is correct according authentication + if (auth_level == LEVEL_ADMIN) { + path = "/"; + } else { + path = "/user"; + } + //get current path + if (_webserver->hasArg ("path") ) { + path += _webserver->arg ("path") ; + } + //to have a clean path + path.trim(); + path.replace ("//", "/"); + if (path[path.length() - 1] != '/') { + path += "/"; + } + //check if query need some action + if (_webserver->hasArg ("action") ) { + //delete a file + if (_webserver->arg ("action") == "delete" && _webserver->hasArg ("filename") ) { + String filename; + String shortname = _webserver->arg ("filename"); + shortname.replace ("/", ""); + filename = path + _webserver->arg ("filename"); + filename.replace ("//", "/"); + if (!SPIFFS.exists (filename) ) { + status = shortname + " does not exists!"; + } else { + if (SPIFFS.remove (filename) ) { + status = shortname + " deleted"; + //what happen if no "/." and no other subfiles ? + String ptmp = path; + if ( (path != "/") && (path[path.length() - 1] = '/') ) { + ptmp = path.substring (0, path.length() - 1); + } + File dir = SPIFFS.open (ptmp); + File dircontent = dir.openNextFile(); + if (!dircontent) { + //keep directory alive even empty + File r = SPIFFS.open (path + "/.", FILE_WRITE); + if (r) { + r.close(); + } + } + } else { + status = "Cannot deleted " ; + status += shortname ; + } + } + } + //delete a directory + if (_webserver->arg ("action") == "deletedir" && _webserver->hasArg ("filename") ) { + String filename; + String shortname = _webserver->arg ("filename"); + shortname.replace ("/", ""); + filename = path + _webserver->arg ("filename"); + filename += "/"; + filename.replace ("//", "/"); + if (filename != "/") { + bool delete_error = false; + File dir = SPIFFS.open (path + shortname); + { + File file2deleted = dir.openNextFile(); + while (file2deleted) { + String fullpath = file2deleted.name(); + if (!SPIFFS.remove (fullpath) ) { + delete_error = true; + status = "Cannot deleted " ; + status += fullpath; + } + file2deleted = dir.openNextFile(); + } + } + if (!delete_error) { + status = shortname ; + status += " deleted"; + } + } + } + //create a directory + if (_webserver->arg ("action") == "createdir" && _webserver->hasArg ("filename") ) { + String filename; + filename = path + _webserver->arg ("filename") + "/."; + String shortname = _webserver->arg ("filename"); + shortname.replace ("/", ""); + filename.replace ("//", "/"); + if (SPIFFS.exists (filename) ) { + status = shortname + " already exists!"; + } else { + File r = SPIFFS.open (filename, FILE_WRITE); + if (!r) { + status = "Cannot create "; + status += shortname ; + } else { + r.close(); + status = shortname + " created"; + } + } + } + } + String jsonfile = "{"; + String ptmp = path; + if ( (path != "/") && (path[path.length() - 1] = '/') ) { + ptmp = path.substring (0, path.length() - 1); + } + File dir = SPIFFS.open (ptmp); + jsonfile += "\"files\":["; + bool firstentry = true; + String subdirlist = ""; + File fileparsed = dir.openNextFile(); + while (fileparsed) { + String filename = fileparsed.name(); + String size = ""; + bool addtolist = true; + //remove path from name + filename = filename.substring (path.length(), filename.length() ); + //check if file or subfile + if (filename.indexOf ("/") > -1) { + //Do not rely on "/." to define directory as SPIFFS upload won't create it but directly files + //and no need to overload SPIFFS if not necessary to create "/." if no need + //it will reduce SPIFFS available space so limit it to creation + filename = filename.substring (0, filename.indexOf ("/") ); + String tag = "*"; + tag += filename + "*"; + if (subdirlist.indexOf (tag) > -1 || filename.length() == 0) { //already in list + addtolist = false; //no need to add + } else { + size = "-1"; //it is subfile so display only directory, size will be -1 to describe it is directory + if (subdirlist.length() == 0) { + subdirlist += "*"; + } + subdirlist += filename + "*"; //add to list + } + } else { + //do not add "." file + if (! ( (filename == ".") || (filename == "") ) ) { + size = ESPResponseStream::formatBytes (fileparsed.size() ); + } else { + addtolist = false; + } + } + if (addtolist) { + if (!firstentry) { + jsonfile += ","; + } else { + firstentry = false; + } + jsonfile += "{"; + jsonfile += "\"name\":\""; + jsonfile += filename; + jsonfile += "\",\"size\":\""; + jsonfile += size; + jsonfile += "\""; + jsonfile += "}"; + } + fileparsed = dir.openNextFile(); + } + jsonfile += "],"; + jsonfile += "\"path\":\"" + path + "\","; + jsonfile += "\"status\":\"" + status + "\","; + size_t totalBytes; + size_t usedBytes; + totalBytes = SPIFFS.totalBytes(); + usedBytes = SPIFFS.usedBytes(); + jsonfile += "\"total\":\"" + ESPResponseStream::formatBytes (totalBytes) + "\","; + jsonfile += "\"used\":\"" + ESPResponseStream::formatBytes (usedBytes) + "\","; + jsonfile.concat (F ("\"occupation\":\"") ); + jsonfile += String (100 * usedBytes / totalBytes); + jsonfile += "\""; + jsonfile += "}"; + path = ""; + _webserver->sendHeader("Cache-Control", "no-cache"); + _webserver->send(200, "application/json", jsonfile); + _upload_status = UPLOAD_STATUS_NONE; +} + +//push error code and message to websocket +void Web_Server::pushError(int code, const char * st, bool web_error, uint16_t timeout){ + if (_socket_server && st) { + String s = "ERROR:" + String(code) + ":"; + s+=st; + _socket_server->sendTXT(_id_connection, s); + if (web_error != 0) { + if (_webserver) { + if (_webserver->client().available() > 0) { + _webserver->send (web_error, "text/xml", st); + } + } + } + uint32_t t = millis(); + while (millis() - t < timeout) { + _socket_server->loop(); + delay(10); + } + } +} + +//abort reception of packages +void Web_Server::cancelUpload(){ + if (_webserver) { + if (_webserver->client().available() > 0) { + HTTPUpload& upload = _webserver->upload(); + upload.status = UPLOAD_FILE_ABORTED; + errno = ECONNABORTED; + _webserver->client().stop(); + delay(100); + } + } +} + +//SPIFFS files uploader handle +void Web_Server::SPIFFSFileupload () +{ + static String filename; + static File fsUploadFile = (File)0; + //get authentication status + level_authenticate_type auth_level= is_authenticated(); + //Guest cannot upload - only admin + if (auth_level == LEVEL_GUEST) { + _upload_status = UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload rejected]\r\n"); + pushError(ESP_ERROR_AUTHENTICATION, "Upload rejected", 401); + } else { + HTTPUpload& upload = _webserver->upload(); + if((_upload_status != UPLOAD_STATUS_FAILED)|| (upload.status == UPLOAD_FILE_START)){ + //Upload start + //************** + if(upload.status == UPLOAD_FILE_START) { + _upload_status= UPLOAD_STATUS_ONGOING; + String upload_filename = upload.filename; + if (upload_filename[0] != '/') filename = "/" + upload_filename; + else filename = upload.filename; + //according User or Admin the root is different as user is isolate to /user when admin has full access + if(auth_level != LEVEL_ADMIN) { + upload_filename = filename; + filename = "/user" + upload_filename; + } + + if (SPIFFS.exists (filename) ) { + SPIFFS.remove (filename); + } + if (fsUploadFile ) { + fsUploadFile.close(); + } + String sizeargname = upload.filename + "S"; + if (_webserver->hasArg (sizeargname.c_str()) ) { + uint32_t filesize = _webserver->arg (sizeargname.c_str()).toInt(); + uint32_t freespace = SPIFFS.totalBytes() - SPIFFS.usedBytes(); + if (filesize > freespace) { + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload error]\r\n"); + pushError(ESP_ERROR_NOT_ENOUGH_SPACE, "Upload rejected, not enough space"); + } + + } + if (_upload_status != UPLOAD_STATUS_FAILED) { + //create file + fsUploadFile = SPIFFS.open(filename, FILE_WRITE); + //check If creation succeed + if (fsUploadFile) { + //if yes upload is started + _upload_status= UPLOAD_STATUS_ONGOING; + } else { + //if no set cancel flag + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload error]\r\n"); + pushError(ESP_ERROR_FILE_CREATION, "File creation failed"); + } + } + //Upload write + //************** + } else if(upload.status == UPLOAD_FILE_WRITE) { + vTaskDelay(1 / portTICK_RATE_MS); + //check if file is available and no error + if(fsUploadFile && _upload_status == UPLOAD_STATUS_ONGOING) { + //no error so write post date + if (upload.currentSize != fsUploadFile.write(upload.buf, upload.currentSize)) { + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload error]\r\n"); + pushError(ESP_ERROR_FILE_WRITE, "File write failed"); + } + } else { + //we have a problem set flag UPLOAD_STATUS_FAILED + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload error]\r\n"); + pushError(ESP_ERROR_FILE_WRITE, "File write failed"); + } + //Upload end + //************** + } else if(upload.status == UPLOAD_FILE_END) { + //check if file is still open + if(fsUploadFile) { + //close it + fsUploadFile.close(); + //check size + String sizeargname = upload.filename + "S"; + fsUploadFile = SPIFFS.open (filename, FILE_READ); + uint32_t filesize = fsUploadFile.size(); + fsUploadFile.close(); + if (_webserver->hasArg (sizeargname.c_str()) ) { + if (_webserver->arg (sizeargname.c_str()) != String(filesize)) { + _upload_status = UPLOAD_STATUS_FAILED; + } + } + if (_upload_status == UPLOAD_STATUS_ONGOING) { + _upload_status = UPLOAD_STATUS_SUCCESSFUL; + } else { + grbl_send(CLIENT_ALL,"[MSG:Upload error]\r\n"); + pushError(ESP_ERROR_UPLOAD, "File upload failed"); + } + } else { + //we have a problem set flag UPLOAD_STATUS_FAILED + _upload_status=UPLOAD_STATUS_FAILED; + pushError(ESP_ERROR_FILE_CLOSE, "File close failed"); + grbl_send(CLIENT_ALL,"[MSG:Upload error]\r\n"); + + } + //Upload cancelled + //************** + } else { + _upload_status = UPLOAD_STATUS_FAILED; + //pushError(ESP_ERROR_UPLOAD, "File upload failed"); + return; + } + } + } + + if (_upload_status == UPLOAD_STATUS_FAILED) { + cancelUpload(); + if (SPIFFS.exists (filename) ) { + SPIFFS.remove (filename); + } + } + COMMANDS::wait(0); +} + +//Web Update handler +void Web_Server::handleUpdate () +{ + level_authenticate_type auth_level = is_authenticated(); + if (auth_level != LEVEL_ADMIN) { + _upload_status = UPLOAD_STATUS_NONE; + _webserver->send (403, "text/plain", "Not allowed, log in first!\n"); + return; + } + String jsonfile = "{\"status\":\"" ; + jsonfile += String(_upload_status); + jsonfile += "\"}"; + //send status + _webserver->sendHeader("Cache-Control", "no-cache"); + _webserver->send(200, "application/json", jsonfile); + //if success restart + if (_upload_status == UPLOAD_STATUS_SUCCESSFUL) { + COMMANDS::wait(1000); + COMMANDS::restart_ESP(); + } else { + _upload_status = UPLOAD_STATUS_NONE; + } +} + +//File upload for Web update +void Web_Server::WebUpdateUpload () +{ + static size_t last_upload_update; + static uint32_t maxSketchSpace = 0; + //only admin can update FW + if (is_authenticated() != LEVEL_ADMIN) { + _upload_status = UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload rejected]\r\n"); + pushError(ESP_ERROR_AUTHENTICATION, "Upload rejected", 401); + } else { + //get current file ID + HTTPUpload& upload = _webserver->upload(); + if((_upload_status != UPLOAD_STATUS_FAILED)|| (upload.status == UPLOAD_FILE_START)){ + //Upload start + //************** + if(upload.status == UPLOAD_FILE_START) { + grbl_send(CLIENT_ALL,"[MSG:Update Firmware]\r\n"); + _upload_status= UPLOAD_STATUS_ONGOING; + String sizeargname = upload.filename + "S"; + if (_webserver->hasArg (sizeargname.c_str()) ) { + maxSketchSpace = _webserver->arg (sizeargname).toInt(); + } + //check space + size_t flashsize = 0; + if (esp_ota_get_running_partition()) { + const esp_partition_t* partition = esp_ota_get_next_update_partition(NULL); + if (partition) { + flashsize = partition->size; + } + } + if (flashsize < maxSketchSpace) { + pushError(ESP_ERROR_NOT_ENOUGH_SPACE, "Upload rejected, not enough space"); + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Update cancelled]\r\n"); + } + if (_upload_status != UPLOAD_STATUS_FAILED) { + last_upload_update = 0; + if(!Update.begin()) { //start with max available size + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Update cancelled]\r\n"); + pushError(ESP_ERROR_NOT_ENOUGH_SPACE, "Upload rejected, not enough space"); + } else { + grbl_send(CLIENT_ALL,"\n[MSG:Update 0%]\r\n"); + } + } + //Upload write + //************** + } else if(upload.status == UPLOAD_FILE_WRITE) { + vTaskDelay(1 / portTICK_RATE_MS); + //check if no error + if (_upload_status == UPLOAD_STATUS_ONGOING) { + if ( ((100 * upload.totalSize) / maxSketchSpace) !=last_upload_update) { + if ( maxSketchSpace > 0)last_upload_update = (100 * upload.totalSize) / maxSketchSpace; + else last_upload_update = upload.totalSize; + String s = "Update "; + s+= String(last_upload_update); + s+="%"; + grbl_sendf(CLIENT_ALL,"[MSG:%s]\r\n", s.c_str()); + } + if(Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Update write failed]\r\n"); + pushError(ESP_ERROR_FILE_WRITE, "File write failed"); + } + } + //Upload end + //************** + } else if(upload.status == UPLOAD_FILE_END) { + if(Update.end(true)) { //true to set the size to the current progress + //Now Reboot + grbl_send(CLIENT_ALL,"[MSG:Update 100%]\r\n"); + _upload_status=UPLOAD_STATUS_SUCCESSFUL; + } else { + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Update failed]\r\n"); + pushError(ESP_ERROR_UPLOAD, "Update upload failed"); + } + } else if(upload.status == UPLOAD_FILE_ABORTED) { + grbl_send(CLIENT_ALL,"[MSG:Update failed]\r\n"); + _upload_status=UPLOAD_STATUS_FAILED; + return; + } + } + } + if (_upload_status == UPLOAD_STATUS_FAILED) { + cancelUpload(); + Update.end(); + } + COMMANDS::wait(0); +} + + +#ifdef ENABLE_SD_CARD + +//Function to delete not empty directory on SD card +bool Web_Server::deleteRecursive(String path) +{ + bool result = true; + File file = SD.open((char *)path.c_str()); + //failed + if (!file) { + return false; + } + if(!file.isDirectory()) { + file.close(); + //return if success or not + return SD.remove((char *)path.c_str()); + } + file.rewindDirectory(); + while(true) { + File entry = file.openNextFile(); + if (!entry) { + break; + } + String entryPath = entry.name(); + if(entry.isDirectory()) { + entry.close(); + if(!deleteRecursive(entryPath)) { + result = false; + } + } else { + entry.close(); + if (!SD.remove((char *)entryPath.c_str())) { + result = false; + break; + } + } + COMMANDS::wait(0); //wdtFeed + } + file.close(); + if (result) return SD.rmdir((char *)path.c_str()); + else return false; +} + +//direct SD files list////////////////////////////////////////////////// +void Web_Server::handle_direct_SDFileList() +{ + //this is only for admin and user + if (is_authenticated() == LEVEL_GUEST) { + _upload_status=UPLOAD_STATUS_NONE; + _webserver->send(401, "application/json", "{\"status\":\"Authentication failed!\"}"); + return; + } + + String path="/"; + String sstatus="Ok"; + if ((_upload_status == UPLOAD_STATUS_FAILED) || (_upload_status == UPLOAD_STATUS_FAILED)) { + sstatus = "Upload failed"; + _upload_status = UPLOAD_STATUS_NONE; + } + bool list_files = true; + uint64_t totalspace = 0; + uint64_t usedspace = 0; + if (get_sd_state(true) != SDCARD_IDLE) { + _webserver->sendHeader("Cache-Control","no-cache"); + _webserver->send(200, "application/json", "{\"status\":\"No SD Card\"}"); + return; + } + set_sd_state(SDCARD_BUSY_PARSING); + //get current path + if(_webserver->hasArg("path")) { + path += _webserver->arg("path") ; + } + //to have a clean path + path.trim(); + path.replace("//","/"); + if (path[path.length()-1] !='/') { + path +="/"; + } + //check if query need some action + if(_webserver->hasArg("action")) { + //delete a file + if(_webserver->arg("action") == "delete" && _webserver->hasArg("filename")) { + String filename; + String shortname = _webserver->arg("filename"); + filename = path + shortname; + shortname.replace("/",""); + filename.replace("//","/"); + if(!SD.exists((char *)filename.c_str())) { + sstatus = shortname + " does not exist!"; + } else { + if (SD.remove((char *)filename.c_str())) { + sstatus = shortname + " deleted"; + } else { + sstatus = "Cannot deleted " ; + sstatus+=shortname ; + } + } + } + //delete a directory + if( _webserver->arg("action") == "deletedir" && _webserver->hasArg("filename")) { + String filename; + String shortname = _webserver->arg("filename"); + shortname.replace("/",""); + filename = path + "/" + shortname; + filename.replace("//","/"); + if (filename != "/") { + if(!SD.exists((char *)filename.c_str())) { + sstatus = shortname + " does not exist!"; + } else { + if (!deleteRecursive(filename)) { + sstatus ="Error deleting: "; + sstatus += shortname ; + } else { + sstatus = shortname ; + sstatus+=" deleted"; + } + } + } else { + sstatus ="Cannot delete root"; + } + } + //create a directory + if( _webserver->arg("action")=="createdir" && _webserver->hasArg("filename")) { + String filename; + String shortname = _webserver->arg("filename"); + filename = path + shortname; + shortname.replace("/",""); + filename.replace("//","/"); + if(SD.exists((char *)filename.c_str())) { + sstatus = shortname + " already exists!"; + } else { + if (!SD.mkdir((char *)filename.c_str())) { + sstatus = "Cannot create "; + sstatus += shortname ; + } else { + sstatus = shortname + " created"; + } + } + } + } + //check if no need build file list + if( _webserver->hasArg("dontlist")) { + if( _webserver->arg("dontlist") == "yes") { + list_files = false; + } + } + String jsonfile = "{" ; + jsonfile+="\"files\":["; + + if (path!="/")path = path.substring(0,path.length()-1); + if (path!="/" && !SD.exists((char *)path.c_str())) { + + String s = "{\"status\":\" "; + s += path; + s+= " does not exist on SD Card\"}"; + _webserver->send(200, "application/json", s.c_str()); + return; + } + if (list_files) { + File dir = SD.open((char *)path.c_str()); + if (!dir) { + } + if(!dir.isDirectory()) { + dir.close(); + } + dir.rewindDirectory(); + File entry = dir.openNextFile(); + int i = 0; + while(entry) { + COMMANDS::wait (1); + if (i>0) { + jsonfile+=","; + } + jsonfile+="{\"name\":\""; + String tmpname = entry.name(); + int pos = tmpname.lastIndexOf("/"); + tmpname = tmpname.substring(pos+1); + jsonfile+=tmpname; + jsonfile+="\",\"shortname\":\""; //No need here + jsonfile+=tmpname; + jsonfile+="\",\"size\":\""; + if (entry.isDirectory()) { + jsonfile+="-1"; + } else { + // files have sizes, directories do not + jsonfile+=ESPResponseStream::formatBytes(entry.size()); + } + jsonfile+="\",\"datetime\":\""; + //TODO - can be done later + jsonfile+="\"}"; + i++; + entry.close(); + entry = dir.openNextFile(); + } + dir.close(); + } + jsonfile+="],\"path\":\""; + jsonfile+=path + "\","; + jsonfile+="\"total\":\""; + String stotalspace,susedspace; + //SDCard are in GB or MB but no less + totalspace = SD.totalBytes(); + usedspace = SD.usedBytes(); + stotalspace = ESPResponseStream::formatBytes(totalspace); + susedspace = ESPResponseStream::formatBytes(usedspace+1); + + uint32_t occupedspace = 1; + uint32_t usedspace2 = usedspace/(1024*1024); + uint32_t totalspace2 = totalspace/(1024*1024); + occupedspace = (usedspace2 * 100)/totalspace2; + //minimum if even one byte is used is 1% + if ( occupedspace <= 1) { + occupedspace=1; + } + if (totalspace) { + jsonfile+= stotalspace ; + } else { + jsonfile+= "-1"; + } + jsonfile+="\",\"used\":\""; + jsonfile+= susedspace ; + jsonfile+="\",\"occupation\":\""; + if (totalspace) { + jsonfile+= String(occupedspace); + } else { + jsonfile+= "-1"; + } + jsonfile+= "\","; + jsonfile+= "\"mode\":\"direct\","; + jsonfile+= "\"status\":\""; + jsonfile+=sstatus + "\""; + jsonfile+= "}"; + _webserver->sendHeader("Cache-Control","no-cache"); + _webserver->send (200, "application/json", jsonfile.c_str()); + _upload_status=UPLOAD_STATUS_NONE; + set_sd_state(SDCARD_IDLE); +} + +//SD File upload with direct access to SD/////////////////////////////// +void Web_Server::SDFile_direct_upload() +{ + static String filename ; + static File sdUploadFile; + //this is only for admin and user + if (is_authenticated() == LEVEL_GUEST) { + _upload_status=UPLOAD_STATUS_FAILED; + _webserver->send(401, "application/json", "{\"status\":\"Authentication failed!\"}"); + pushError(ESP_ERROR_AUTHENTICATION, "Upload rejected", 401); + } else { + //retrieve current file id + HTTPUpload& upload = _webserver->upload(); + if((_upload_status != UPLOAD_STATUS_FAILED)|| (upload.status == UPLOAD_FILE_START)){ + //Upload start + //************** + if(upload.status == UPLOAD_FILE_START) { + _upload_status= UPLOAD_STATUS_ONGOING; + filename= upload.filename; + //on SD need to add / if not present + if (filename[0]!='/') { + filename= "/"+upload.filename; + } + //check if SD Card is available + if ( get_sd_state(true) != SDCARD_IDLE) { + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload cancelled]\r\n"); + pushError(ESP_ERROR_UPLOAD_CANCELLED, "Upload cancelled"); + + } else { + set_sd_state(SDCARD_BUSY_UPLOADING); + //delete file on SD Card if already present + if(SD.exists((char *)filename.c_str())) { + SD.remove((char *)filename.c_str()); + } + String sizeargname = upload.filename + "S"; + if (_webserver->hasArg (sizeargname.c_str()) ) { + uint32_t filesize = _webserver->arg (sizeargname.c_str()).toInt(); + uint64_t freespace = SD.totalBytes() - SD.usedBytes(); + if (filesize > freespace) { + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload error]\r\n"); + pushError(ESP_ERROR_NOT_ENOUGH_SPACE, "Upload rejected, not enough space"); + } + + } + if (_upload_status != UPLOAD_STATUS_FAILED){ + //Create file for writing + sdUploadFile = SD.open((char *)filename.c_str(), FILE_WRITE); + //check if creation succeed + if (!sdUploadFile) { + //if creation failed + _upload_status=UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload failed]\r\n"); + pushError(ESP_ERROR_FILE_CREATION, "File creation failed"); + } + //if creation succeed set flag UPLOAD_STATUS_ONGOING + else { + _upload_status= UPLOAD_STATUS_ONGOING; + } + } + } + //Upload write + //************** + } else if(upload.status == UPLOAD_FILE_WRITE) { + vTaskDelay(1 / portTICK_RATE_MS); + if(sdUploadFile && (_upload_status == UPLOAD_STATUS_ONGOING) && (get_sd_state(false) == SDCARD_BUSY_UPLOADING)) { + //no error write post data + if (upload.currentSize != sdUploadFile.write(upload.buf, upload.currentSize)) { + _upload_status = UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload failed]\r\n"); + pushError(ESP_ERROR_FILE_WRITE, "File write failed"); + } + } else { //if error set flag UPLOAD_STATUS_FAILED + _upload_status = UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload failed]\r\n"); + pushError(ESP_ERROR_FILE_WRITE, "File write failed"); + } + //Upload end + //************** + } else if(upload.status == UPLOAD_FILE_END) { + //if file is open close it + if(sdUploadFile) { + sdUploadFile.close(); + //TODO Check size + String sizeargname = upload.filename + "S"; + if (_webserver->hasArg (sizeargname.c_str()) ) { + uint32_t filesize = 0; + sdUploadFile = SD.open (filename.c_str(), FILE_READ); + filesize = sdUploadFile.size(); + sdUploadFile.close(); + if (_webserver->arg (sizeargname.c_str()) != String(filesize)) { + _upload_status = UPLOAD_STATUS_FAILED; + pushError(ESP_ERROR_UPLOAD, "File upload mismatch"); + grbl_send(CLIENT_ALL,"[MSG:Upload failed]\r\n"); + } + } + } else { + _upload_status = UPLOAD_STATUS_FAILED; + grbl_send(CLIENT_ALL,"[MSG:Upload failed]\r\n"); + pushError(ESP_ERROR_FILE_CLOSE, "File close failed"); + } + if (_upload_status == UPLOAD_STATUS_ONGOING) { + _upload_status = UPLOAD_STATUS_SUCCESSFUL; + set_sd_state(SDCARD_IDLE); + } else { + _upload_status = UPLOAD_STATUS_FAILED; + pushError(ESP_ERROR_UPLOAD, "Upload error"); + } + + } else {//Upload cancelled + _upload_status=UPLOAD_STATUS_FAILED; + set_sd_state(SDCARD_IDLE); + grbl_send(CLIENT_ALL,"[MSG:Upload failed]\r\n"); + if(sdUploadFile) { + sdUploadFile.close(); + } + return; + } + } + } + if (_upload_status == UPLOAD_STATUS_FAILED) { + cancelUpload(); + if(sdUploadFile) { + sdUploadFile.close(); + } + if(SD.exists((char *)filename.c_str())) { + SD.remove((char *)filename.c_str()); + } + set_sd_state(SDCARD_IDLE); + } + COMMANDS::wait(0); +} +#endif + +void Web_Server::handle(){ +static uint32_t timeout = millis(); + COMMANDS::wait(0); +#ifdef ENABLE_CAPTIVE_PORTAL + if(WiFi.getMode() != WIFI_STA){ + dnsServer.processNextRequest(); + } +#endif + if (_webserver)_webserver->handleClient(); + if (_socket_server && _setupdone)_socket_server->loop(); + if ((millis() - timeout) > 10000) { + if (_socket_server){ + String s = "PING:"; + s+=String(_id_connection); + _socket_server->broadcastTXT(s); + timeout=millis(); + } + } + +} + + +void Web_Server::handle_Websocket_Event(uint8_t num, uint8_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + //USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = _socket_server->remoteIP(num); + //USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + String s = "CURRENT_ID:" + String(num); + // send message to client + _id_connection = num; + _socket_server->sendTXT(_id_connection, s); + s = "ACTIVE_ID:" + String(_id_connection); + _socket_server->broadcastTXT(s); + } + break; + case WStype_TEXT: + //USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + //USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + //hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + default: + break; + } + +} + +String Web_Server::get_Splited_Value(String data, char separator, int index) +{ + int found = 0; + int strIndex[] = {0, -1}; + int maxIndex = data.length()-1; + + for(int i=0; i<=maxIndex && found<=index; i++){ + if(data.charAt(i)==separator || i==maxIndex){ + found++; + strIndex[0] = strIndex[1]+1; + strIndex[1] = (i == maxIndex) ? i+1 : i; + } + } + + return found>index ? data.substring(strIndex[0], strIndex[1]) : ""; +} + + +//helper to extract content type from file extension +//Check what is the content tye according extension file +String Web_Server::getContentType (String filename) +{ + String file_name = filename; + file_name.toLowerCase(); + if (filename.endsWith (".htm") ) { + return "text/html"; + } else if (file_name.endsWith (".html") ) { + return "text/html"; + } else if (file_name.endsWith (".css") ) { + return "text/css"; + } else if (file_name.endsWith (".js") ) { + return "application/javascript"; + } else if (file_name.endsWith (".png") ) { + return "image/png"; + } else if (file_name.endsWith (".gif") ) { + return "image/gif"; + } else if (file_name.endsWith (".jpeg") ) { + return "image/jpeg"; + } else if (file_name.endsWith (".jpg") ) { + return "image/jpeg"; + } else if (file_name.endsWith (".ico") ) { + return "image/x-icon"; + } else if (file_name.endsWith (".xml") ) { + return "text/xml"; + } else if (file_name.endsWith (".pdf") ) { + return "application/x-pdf"; + } else if (file_name.endsWith (".zip") ) { + return "application/x-zip"; + } else if (file_name.endsWith (".gz") ) { + return "application/x-gzip"; + } else if (file_name.endsWith (".txt") ) { + return "text/plain"; + } + return "application/octet-stream"; +} + +//check authentification +level_authenticate_type Web_Server::is_authenticated() +{ +#ifdef ENABLE_AUTHENTICATION + if (_webserver->hasHeader ("Cookie") ) { + String cookie = _webserver->header ("Cookie"); + int pos = cookie.indexOf ("ESPSESSIONID="); + if (pos != -1) { + int pos2 = cookie.indexOf (";", pos); + String sessionID = cookie.substring (pos + strlen ("ESPSESSIONID="), pos2); + IPAddress ip = _webserver->client().remoteIP(); + //check if cookie can be reset and clean table in same time + return ResetAuthIP (ip, sessionID.c_str() ); + } + } + return LEVEL_GUEST; +#else + return LEVEL_ADMIN; +#endif +} + +#ifdef ENABLE_AUTHENTICATION + +//add the information in the linked list if possible +bool Web_Server::AddAuthIP (auth_ip * item) +{ + if (_nb_ip > MAX_AUTH_IP) { + return false; + } + item->_next = _head; + _head = item; + _nb_ip++; + return true; +} + +//Session ID based on IP and time using 16 char +char * Web_Server::create_session_ID() +{ + static char sessionID[17]; +//reset SESSIONID + for (int i = 0; i < 17; i++) { + sessionID[i] = '\0'; + } +//get time + uint32_t now = millis(); +//get remote IP + IPAddress remoteIP = _webserver->client().remoteIP(); +//generate SESSIONID + if (0 > sprintf (sessionID, "%02X%02X%02X%02X%02X%02X%02X%02X", remoteIP[0], remoteIP[1], remoteIP[2], remoteIP[3], (uint8_t) ( (now >> 0) & 0xff), (uint8_t) ( (now >> 8) & 0xff), (uint8_t) ( (now >> 16) & 0xff), (uint8_t) ( (now >> 24) & 0xff) ) ) { + strcpy (sessionID, "NONE"); + } + return sessionID; +} + + +bool Web_Server::ClearAuthIP (IPAddress ip, const char * sessionID) +{ + auth_ip * current = _head; + auth_ip * previous = NULL; + bool done = false; + while (current) { + if ( (ip == current->ip) && (strcmp (sessionID, current->sessionID) == 0) ) { + //remove + done = true; + if (current == _head) { + _head = current->_next; + _nb_ip--; + delete current; + current = _head; + } else { + previous->_next = current->_next; + _nb_ip--; + delete current; + current = previous->_next; + } + } else { + previous = current; + current = current->_next; + } + } + return done; +} + +//Get info +auth_ip * Web_Server::GetAuth (IPAddress ip, const char * sessionID) +{ + auth_ip * current = _head; + //auth_ip * previous = NULL; + //get time + //uint32_t now = millis(); + while (current) { + if (ip == current->ip) { + if (strcmp (sessionID, current->sessionID) == 0) { + //found + return current; + } + } + //previous = current; + current = current->_next; + } + return NULL; +} + +//Review all IP to reset timers +level_authenticate_type Web_Server::ResetAuthIP (IPAddress ip, const char * sessionID) +{ + auth_ip * current = _head; + auth_ip * previous = NULL; + //get time + //uint32_t now = millis(); + while (current) { + if ( (millis() - current->last_time) > 360000) { + //remove + if (current == _head) { + _head = current->_next; + _nb_ip--; + delete current; + current = _head; + } else { + previous->_next = current->_next; + _nb_ip--; + delete current; + current = previous->_next; + } + } else { + if (ip == current->ip) { + if (strcmp (sessionID, current->sessionID) == 0) { + //reset time + current->last_time = millis(); + return (level_authenticate_type) current->level; + } + } + previous = current; + current = current->_next; + } + } + return LEVEL_GUEST; +} +#endif + +#endif // Enable HTTP && ENABLE_WIFI + +#endif // ARDUINO_ARCH_ESP32 diff --git a/Grbl_Esp32-master/Grbl_Esp32/web_server.h b/Grbl_Esp32-master/Grbl_Esp32/web_server.h new file mode 100644 index 0000000..c96930b --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/web_server.h @@ -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 + diff --git a/Grbl_Esp32-master/Grbl_Esp32/wificonfig.cpp b/Grbl_Esp32-master/Grbl_Esp32/wificonfig.cpp new file mode 100644 index 0000000..eec95e6 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/wificonfig.cpp @@ -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 +#include +#include +#include +#include +#include +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/wificonfig.h b/Grbl_Esp32-master/Grbl_Esp32/wificonfig.h new file mode 100644 index 0000000..a4e82f7 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/wificonfig.h @@ -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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/wifiservices.cpp b/Grbl_Esp32-master/Grbl_Esp32/wifiservices.cpp new file mode 100644 index 0000000..4255a34 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/wifiservices.cpp @@ -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 +#include +#include +#include +#include "report.h" +#include "wificonfig.h" +#include "wifiservices.h" +#ifdef ENABLE_MDNS +#include +#endif +#ifdef ENABLE_OTA +#include +#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 diff --git a/Grbl_Esp32-master/Grbl_Esp32/wifiservices.h b/Grbl_Esp32-master/Grbl_Esp32/wifiservices.h new file mode 100644 index 0000000..790fd67 --- /dev/null +++ b/Grbl_Esp32-master/Grbl_Esp32/wifiservices.h @@ -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 + diff --git a/Grbl_Esp32-master/LICENSE b/Grbl_Esp32-master/LICENSE new file mode 100644 index 0000000..9e419e0 --- /dev/null +++ b/Grbl_Esp32-master/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. \ No newline at end of file diff --git a/Grbl_Esp32-master/README.md b/Grbl_Esp32-master/README.md new file mode 100644 index 0000000..460749d --- /dev/null +++ b/Grbl_Esp32-master/README.md @@ -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. + + + +### 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) diff --git a/Grbl_Esp32-master/command.sh b/Grbl_Esp32-master/command.sh new file mode 100644 index 0000000..e366f20 --- /dev/null +++ b/Grbl_Esp32-master/command.sh @@ -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 +} diff --git a/Grbl_Esp32-master/doc/Commands.txt b/Grbl_Esp32-master/doc/Commands.txt new file mode 100644 index 0000000..2071a42 --- /dev/null +++ b/Grbl_Esp32-master/doc/Commands.txt @@ -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]pwd= + +* Set STA Password +[ESP101]pwd= + +* Set/Get STA IP mode (DHCP/STATIC) +[ESP102]pwd= + +* Set/Get STA IP/Mask/GW +[ESP103]IP= MSK= GW= pwd= + +* Set/Get AP SSID +[ESP105]pwd= + +* Change AP Password +[ESP106]pwd= + +* Set/Get AP IP +[ESP107]pwd= + +* Set/Get AP channel +[ESP108]pwd= + +* Set/Get radio state which can be STA, AP, BT, OFF +[ESP110]pwd= + +* Get current IP +[ESP111]
+ +* Get/Set hostname +[ESP112] pwd= + +* Get/Set immediate Radio (WiFi/BT) state which can be ON, OFF +[ESP115]pwd= + +* Get/Set HTTP state which can be ON, OFF +[ESP120]pwd= + +* Get/Set HTTP port +[ESP121]pwd= + +* Get/Set Telnet state which can be ON, OFF +[ESP130]pwd= + +* Get/Set Telnet port +[ESP131]pwd= + +* Get/Set btname +[ESP140]< Bluetooth name> pwd= + +* Get SD Card Status +[ESP200] pwd= + +* Get SD Card Content +[ESP210] pwd= + +* Delete SD Card file / directory +[ESP215]pwd= + +* Print SD file +[ESP220] pwd= + +*Get full EEPROM settings content +but do not give any passwords +[ESP400] pwd= + +*Set EEPROM setting +position in EEPROM, type: B(byte), I(integer/long), S(string), A(IP address / mask) +[ESP401]P= T= V= pwd= +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= + +*Get current settings of ESP3D +output is JSON or plain text according parameter +[ESP420]pwd= + +* Restart ESP +[ESP444]RESTART pwd= + +* Change / Reset user password +[ESP555]pwd= +if no password set it use default one + +* Send Notification +[ESP600]msg [pwd=] + +* Set/Get Notification settings +[ESP610]type= T1= T2= TS= [pwd=] +Get will give type and settings only, not the protected T1/T2 + +* Read SPIFFS file and send each line to serial +[ESP700] pwd= + +* Format SPIFFS +[ESP710]FORMAT pwd= + +* SPIFFS total size and used size +[ESP720]
pwd= + +* Get fw version and basic information +[ESP800]
+ + diff --git a/Grbl_Esp32-master/doc/csv/alarm_codes_en_US.csv b/Grbl_Esp32-master/doc/csv/alarm_codes_en_US.csv new file mode 100644 index 0000000..1ab2b8a --- /dev/null +++ b/Grbl_Esp32-master/doc/csv/alarm_codes_en_US.csv @@ -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." diff --git a/Grbl_Esp32-master/doc/csv/build_option_codes_en_US.csv b/Grbl_Esp32-master/doc/csv/build_option_codes_en_US.csv new file mode 100644 index 0000000..9a6d0fb --- /dev/null +++ b/Grbl_Esp32-master/doc/csv/build_option_codes_en_US.csv @@ -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 \ No newline at end of file diff --git a/Grbl_Esp32-master/doc/csv/error_codes_en_US.csv b/Grbl_Esp32-master/doc/csv/error_codes_en_US.csv new file mode 100644 index 0000000..b64832b --- /dev/null +++ b/Grbl_Esp32-master/doc/csv/error_codes_en_US.csv @@ -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" diff --git a/Grbl_Esp32-master/doc/csv/setting_codes_en_US.csv b/Grbl_Esp32-master/doc/csv/setting_codes_en_US.csv new file mode 100644 index 0000000..5536262 --- /dev/null +++ b/Grbl_Esp32-master/doc/csv/setting_codes_en_US.csv @@ -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" + diff --git a/Grbl_Esp32-master/doc/script/fit_nonlinear_spindle.py b/Grbl_Esp32-master/doc/script/fit_nonlinear_spindle.py new file mode 100644 index 0000000..57ca5f3 --- /dev/null +++ b/Grbl_Esp32-master/doc/script/fit_nonlinear_spindle.py @@ -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_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_min), + (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_min), + (x=PWM_point1), + (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") diff --git a/Grbl_Esp32-master/doc/script/piecewise_example.png b/Grbl_Esp32-master/doc/script/piecewise_example.png new file mode 100644 index 0000000..83b24d6 Binary files /dev/null and b/Grbl_Esp32-master/doc/script/piecewise_example.png differ diff --git a/Grbl_Esp32-master/doc/script/simple_stream.py b/Grbl_Esp32-master/doc/script/simple_stream.py new file mode 100644 index 0000000..67c2a2c --- /dev/null +++ b/Grbl_Esp32-master/doc/script/simple_stream.py @@ -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 to exit and disable grbl.") + +# Close file and serial port +f.close() +s.close() \ No newline at end of file diff --git a/Grbl_Esp32-master/doc/script/stream.py b/Grbl_Esp32-master/doc/script/stream.py new file mode 100644 index 0000000..4a637ab --- /dev/null +++ b/Grbl_Esp32-master/doc/script/stream.py @@ -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 to exit and disable Grbl.") + +# Close file and serial port +f.close() +s.close() diff --git a/Grbl_Esp32-master/embedded/build.bat b/Grbl_Esp32-master/embedded/build.bat new file mode 100644 index 0000000..85b2a5f --- /dev/null +++ b/Grbl_Esp32-master/embedded/build.bat @@ -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 + diff --git a/Grbl_Esp32-master/embedded/embedded.h b/Grbl_Esp32-master/embedded/embedded.h new file mode 100644 index 0000000..d4b125a --- /dev/null +++ b/Grbl_Esp32-master/embedded/embedded.h @@ -0,0 +1,427 @@ +/* Generated by bin2c, do not edit manually */ + +/* Contents of file tool.html.gz */ +#define tool_html_gz_size 6728 +const unsigned char tool_html_gz[6728] = { + 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 +}; diff --git a/Grbl_Esp32-master/embedded/footer.txt b/Grbl_Esp32-master/embedded/footer.txt new file mode 100644 index 0000000..327a2c0 --- /dev/null +++ b/Grbl_Esp32-master/embedded/footer.txt @@ -0,0 +1 @@ +#endif //__nofile_h diff --git a/Grbl_Esp32-master/embedded/gulpfile.js b/Grbl_Esp32-master/embedded/gulpfile.js new file mode 100644 index 0000000..8deb4a7 --- /dev/null +++ b/Grbl_Esp32-master/embedded/gulpfile.js @@ -0,0 +1,125 @@ +var gulp = require('gulp'), + jshint = require('gulp-jshint'), + gulpif = require('gulp-if'), + concat = require('gulp-concat'), + uglify = require('gulp-uglify'), + cleanCSS = require('gulp-clean-css'), + removeCode = require('gulp-remove-code'), + merge = require('merge-stream'), + del = require('del'), + zip = require('gulp-zip'), + gzip = require('gulp-gzip'), + htmlmin = require('gulp-htmlmin'), + replace = require('gulp-replace'), + fs = require('fs'), + smoosher = require('gulp-smoosher'); + +var demoMode = false; +var testMode = false; + +function clean() { + return del(['dist']); +} + +function clean2() { + return del(['dist/js', 'dist/css']); +} +function lint() { + return gulp.src('www/js/**/script.js') + .pipe(jshint()) + .pipe(jshint.reporter('default')); +} + +function Copytest() { + return merge( + gulp.src(['www/tool.html']) + .pipe(removeCode({production: false})) + .pipe(gulp.dest('dist')), + gulp.src(['www/images/**/*.*']) + .pipe(gulp.dest('dist/images')) + ) +} + +function Copy() { + return merge( + gulp.src(['www/tool.html']) + .pipe(removeCode({production: true})) + .pipe(gulp.dest('dist')), + gulp.src(['www/images/**/*.*']) + .pipe(gulp.dest('dist/images')) + ) +} + +function concatApptest() { + return merge( + gulp.src([ 'www/js/**/*.js']) + .pipe(concat('script.js')) + .pipe(removeCode({production: false})) + .pipe(gulp.dest('./dist/js')), + + gulp.src([ 'www/css/**/*.css']) + .pipe(concat('style.css')) + .pipe(gulp.dest('./dist/css/')) + ) +} + +function concatApp() { + return merge( + gulp.src([ 'www/js/**/*.js']) + .pipe(concat('script.js')) + .pipe(removeCode({production: true})) + .pipe(gulp.dest('./dist/js')), + + gulp.src([ 'www/css/**/*.css']) + .pipe(concat('style.css')) + .pipe(gulp.dest('./dist/css/')) + ) +} + +function minifyApp() { + return merge( + gulp.src(['dist/js/script.js']) + .pipe(uglify({mangle: true})) + .pipe(gulp.dest('./dist/js/')), + + gulp.src('dist/css/style.css') + .pipe(cleanCSS({debug: true}, function(details) { + console.log(details.name + ': ' + details.stats.originalSize); + console.log(details.name + ': ' + details.stats.minifiedSize); + })) + .pipe(gulp.dest('./dist/css/')), + + gulp.src('dist/tool.html') + .pipe(htmlmin({collapseWhitespace: true, minifyCSS: true})) + .pipe(gulp.dest('dist')) + ) +} + +function smoosh() { + return gulp.src('dist/tool.html') + .pipe(smoosher()) + .pipe(gulp.dest('dist')) +} + +function compress() { + return gulp.src('dist/tool.html') + .pipe(gzip()) + .pipe(gulp.dest('.')); +} + +gulp.task(clean); +gulp.task(lint); +gulp.task(Copy); +gulp.task(Copytest); +gulp.task(concatApp); +gulp.task(concatApptest); +gulp.task(minifyApp); +gulp.task(smoosh); +gulp.task(clean2); + +var defaultSeries = gulp.series(clean, lint, Copy, concatApp, smoosh); +var packageSeries = gulp.series(clean, lint, Copy, concatApp,minifyApp, smoosh, compress, clean2); + +gulp.task('default', defaultSeries); +gulp.task('package', packageSeries); + diff --git a/Grbl_Esp32-master/embedded/header.txt b/Grbl_Esp32-master/embedded/header.txt new file mode 100644 index 0000000..85c9dfe --- /dev/null +++ b/Grbl_Esp32-master/embedded/header.txt @@ -0,0 +1,24 @@ +/* + 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 diff --git a/Grbl_Esp32-master/embedded/package-lock.json b/Grbl_Esp32-master/embedded/package-lock.json new file mode 100644 index 0000000..156dfe5 --- /dev/null +++ b/Grbl_Esp32-master/embedded/package-lock.json @@ -0,0 +1,6263 @@ +{ + "name": "embedded4ESP3D", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@nodelib/fs.scandir": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.2.tgz", + "integrity": "sha512-wrIBsjA5pl13f0RN4Zx4FNWmU71lv03meGKnqRUoCyan17s4V3WL92f3w3AIuWbNnpcrQyFBU5qMavJoB8d27w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.2", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.2.tgz", + "integrity": "sha512-z8+wGWV2dgUhLqrtRYa03yDx4HWMvXKi1z8g3m2JyxAx8F7xk74asqPk5LAETjqDSGLFML/6CDl0+yFunSYicw==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.3.tgz", + "integrity": "sha512-l6t8xEhfK9Sa4YO5mIRdau7XSOADfmh3jCr0evNHdY+HNkW6xuQhgMH7D73VV6WpZOagrW0UludvMTiifiwTfA==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.2", + "fastq": "^1.6.0" + } + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "12.7.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.3.tgz", + "integrity": "sha512-3SiLAIBkDWDg6vFo0+5YJyHPWU9uwu40Qe+v+0MH8wRKYBimHvvAOyk3EzMrD/TrIlLYfXrqDqrg913PynrMJQ==", + "dev": true + }, + "CSSselect": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/CSSselect/-/CSSselect-0.4.1.tgz", + "integrity": "sha1-+Kt+H4QYzmPNput713ioXX7EkrI=", + "dev": true, + "requires": { + "CSSwhat": "0.4", + "domutils": "1.4" + } + }, + "CSSwhat": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/CSSwhat/-/CSSwhat-0.4.7.tgz", + "integrity": "sha1-hn2g/zn3eGEyQsRM/qg/CqTr35s=", + "dev": true + }, + "acorn": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.6.4.tgz", + "integrity": "sha1-6x9FtKQ/ox0DcBpexG87Umc+kO4=", + "dev": true + }, + "aggregate-error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.0.tgz", + "integrity": "sha512-yKD9kEoJIR+2IFqhMwayIBgheLYbB3PS2OBhWae1L/ODTd/JF/30cW0bc9TqzRL3k4U41Dieu3BF4I29p8xesA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^3.2.0" + } + }, + "alter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "integrity": "sha1-x1iICGF1cgNKrmJICvJrHU0cs80=", + "dev": true, + "requires": { + "stable": "~0.1.3" + } + }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "dev": true, + "requires": { + "buffer-equal": "^1.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "dev": true, + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-replace/-/async-replace-1.0.1.tgz", + "integrity": "sha1-0/CFfM0C8elOsUnLX4nVisTwIdY=", + "dev": true, + "requires": { + "async": "^1.4.2" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + } + } + }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "dev": true, + "requires": { + "async-done": "^1.2.2" + } + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "dev": true, + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "binaryextensions": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.2.tgz", + "integrity": "sha512-xVNN69YGDghOqCCtA6FI7avYrr02mTJjOgB0/f1VPD3pJC8QEvjTKWc4epDx8AqxxA75NI0QpVM2gPJXUbE4Tg==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "bufferstreams": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-1.1.3.tgz", + "integrity": "sha512-HaJnVuslRF4g2kSDeyl++AaVizoitCpL9PglzCYwy0uHHyvWerfvEb8jWmYbF1z4kiVFolGomnxSGl+GUQp2jg==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "cdnizer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/cdnizer/-/cdnizer-3.0.2.tgz", + "integrity": "sha512-XTBGCv61pn9r8o/HDkUlPLv9hQ1bx0maWj07UdIU1VV/2P5KAswN9OEHPu6Tz7C5zMXIywdERtpCw0osQYm1ZA==", + "dev": true, + "requires": { + "cdnjs-cdn-data": "^0.1.1", + "google-cdn-data": "^0.1.6", + "jsdelivr-cdn-data": "git://github.com/shahata/jsdelivr-cdn-data.git#d014a2ad1bdfb4c6e3d3cefc7f264435281b91e0", + "lodash": "^4.17.11", + "minimatch": "^3.0.2" + } + }, + "cdnjs-cdn-data": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cdnjs-cdn-data/-/cdnjs-cdn-data-0.1.2.tgz", + "integrity": "sha1-hl00uk5I3Rtz/WaOJKYaWt+biyE=", + "dev": true, + "requires": { + "semver": "~5.0.1" + }, + "dependencies": { + "semver": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", + "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", + "dev": true + } + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cheerio": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.13.1.tgz", + "integrity": "sha1-SK8RNFYbNSf4PZFWxPmo69grBuw=", + "dev": true, + "requires": { + "CSSselect": "~0.4.0", + "entities": "0.x", + "htmlparser2": "~3.4.0", + "underscore": "~1.5" + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "^7.1.1" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "dev": true, + "requires": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-props": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "dev": true, + "requires": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "requires": { + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz", + "integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==", + "dev": true, + "requires": { + "globby": "^10.0.1", + "graceful-fs": "^4.2.2", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.1", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0" + } + }, + "deprecated": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.2.tgz", + "integrity": "sha1-vJ3Pm86RdPz5CQzxKVExxDnGgv0=", + "dev": true + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dom-serializer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", + "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "dev": true + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.2.1.tgz", + "integrity": "sha1-Wd+dzSJ+gIs2Wuc+H2aErD2Ub8I=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.4.3.tgz", + "integrity": "sha1-CGVRN5bGswYDGFDhdVFrr4C3Km8=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "~1.1.9" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "editions": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", + "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "entities": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-0.5.0.tgz", + "integrity": "sha1-9hHLWuIhBQ4AEsZpeVA/164ZzEk=", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.51", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.51.tgz", + "integrity": "sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "fast-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.0.4.tgz", + "integrity": "sha512-wkIbV6qg37xTJwqSsdnIphL1e+LaGz4AIQqr00mIubMaEhv1/HEmJ0uuCGZRNRUkZZmOB5mJKO0ZUTVq+SxMQg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.1", + "@nodelib/fs.walk": "^1.2.1", + "glob-parent": "^5.0.0", + "is-glob": "^4.0.1", + "merge2": "^1.2.3", + "micromatch": "^4.0.2" + } + }, + "fastq": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", + "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==", + "dev": true, + "requires": { + "reusify": "^1.0.0" + } + }, + "filesize": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.1.6.tgz", + "integrity": "sha1-WISSTvyBpkTjcJqsQDIWGDw9eYo=", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "fork-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", + "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=", + "dev": true + }, + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", + "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-watcher": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", + "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "object.defaults": "^1.1.0" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + } + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "google-cdn-data": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/google-cdn-data/-/google-cdn-data-0.1.25.tgz", + "integrity": "sha1-nDwxSasYp8LV7V8PC07ovEWZK3E=", + "dev": true + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "requires": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "dependencies": { + "gulp-cli": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.0.tgz", + "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.1.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.0.1", + "yargs": "^7.1.0" + } + } + } + }, + "gulp-bytediff": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-bytediff/-/gulp-bytediff-1.0.0.tgz", + "integrity": "sha1-VXPidyiwsW1cqIaU/NNhYzj5xS0=", + "dev": true, + "requires": { + "filesize": "~3.1.3", + "gulp-util": "~3.0.6", + "map-stream": "~0.0.6" + } + }, + "gulp-cdnizer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/gulp-cdnizer/-/gulp-cdnizer-2.0.2.tgz", + "integrity": "sha512-kXTdxYMiUoQXvRUaqwm5+uGR2LqUy/7GHde2EtRBK1qY0BUKnbV2mInLBvdsITFQpZ/F6zX2FVFIpLa0qz/8Bw==", + "dev": true, + "requires": { + "cdnizer": "^3.0.2", + "gulp-util": "^3.0.0", + "through2": "^0.5.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "dev": true, + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~3.0.0" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "dev": true + } + } + }, + "gulp-clean-css": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-4.2.0.tgz", + "integrity": "sha512-r4zQsSOAK2UYUL/ipkAVCTRg/2CLZ2A+oPVORopBximRksJ6qy3EX1KGrIWT4ZrHxz3Hlobb1yyJtqiut7DNjA==", + "dev": true, + "requires": { + "clean-css": "4.2.1", + "plugin-error": "1.0.1", + "through2": "3.0.1", + "vinyl-sourcemaps-apply": "0.2.1" + }, + "dependencies": { + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "gulp-concat": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", + "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "dev": true, + "requires": { + "concat-with-sourcemaps": "^1.0.0", + "through2": "^2.0.0", + "vinyl": "^2.0.0" + } + }, + "gulp-gzip": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/gulp-gzip/-/gulp-gzip-1.4.2.tgz", + "integrity": "sha512-ZIxfkUwk2XmZPTT9pPHrHUQlZMyp9nPhg2sfoeN27mBGpi7OaHnOD+WCN41NXjfJQ69lV1nQ9LLm1hYxx4h3UQ==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "bytes": "^3.0.0", + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.0", + "stream-to-array": "^2.3.0", + "through2": "^2.0.3" + } + }, + "gulp-htmlmin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp-htmlmin/-/gulp-htmlmin-5.0.1.tgz", + "integrity": "sha512-ASlyDPZOSKjHYUifYV0rf9JPDflN9IRIb8lw2vRqtYMC4ljU3zAmnnaVXwFQ3H+CfXxZSUesZ2x7jrnPJu93jA==", + "dev": true, + "requires": { + "html-minifier": "^3.5.20", + "plugin-error": "^1.0.1", + "through2": "^2.0.3" + } + }, + "gulp-if": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", + "integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==", + "dev": true, + "requires": { + "gulp-match": "^1.1.0", + "ternary-stream": "^3.0.0", + "through2": "^3.0.1" + }, + "dependencies": { + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "gulp-jshint": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-jshint/-/gulp-jshint-2.1.0.tgz", + "integrity": "sha512-sP3NK8Y/1e58O0PH9t6s7DAr/lKDSUbIY207oWSeufM6/VclB7jJrIBcPCsyhrFTCDUl9DauePbt6VqP2vPM5w==", + "dev": true, + "requires": { + "lodash": "^4.12.0", + "minimatch": "^3.0.3", + "plugin-error": "^0.1.2", + "rcloader": "^0.2.2", + "through2": "^2.0.0" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + } + } + }, + "gulp-match": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", + "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.3" + } + }, + "gulp-ng-annotate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-ng-annotate/-/gulp-ng-annotate-2.1.0.tgz", + "integrity": "sha512-wjazOa5qE83akCih+lK2a0LFvkLbIMeblxr54ofmc3WKJ3Ipx/BM98ZCtCDfQW/008EVUSRqwfEjFKEEGI0QbA==", + "dev": true, + "requires": { + "bufferstreams": "^1.1.0", + "merge": "^1.2.0", + "ng-annotate": "^1.2.1", + "plugin-error": "^0.1.2", + "through2": "^2.0.1", + "vinyl-sourcemaps-apply": "^0.2.1" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + } + } + }, + "gulp-remove-code": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/gulp-remove-code/-/gulp-remove-code-3.0.4.tgz", + "integrity": "sha512-nfGXuE2ra/o008t+XPzd3/dbkgmO4XNLEUibCFlv4KS5+V2cLGU0m9Rmdd4L9ZkduwC1+/AuSEyySt7CZhcLzw==", + "dev": true, + "requires": { + "bufferstreams": "^2.0.1", + "escape-string-regexp": "^1.0.5", + "object.entries": "^1.0.4", + "plugin-error": "^1.0.1", + "through2": "^2.0.3" + }, + "dependencies": { + "bufferstreams": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-2.0.1.tgz", + "integrity": "sha512-ZswyIoBfFb3cVDsnZLLj2IDJ/0ppYdil/v2EGlZXvoefO689FokEmFEldhN5dV7R2QBxFneqTJOMIpfqhj+n0g==", + "dev": true, + "requires": { + "readable-stream": "^2.3.6" + } + } + } + }, + "gulp-replace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", + "integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==", + "dev": true, + "requires": { + "istextorbinary": "2.2.1", + "readable-stream": "^2.0.1", + "replacestream": "^4.0.0" + } + }, + "gulp-smoosher": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/gulp-smoosher/-/gulp-smoosher-0.0.9.tgz", + "integrity": "sha1-IiqiHu5TEzzvKL8ki1xJdbid2x8=", + "dev": true, + "requires": { + "async": "^0.9.0", + "async-replace": "^1.0.0", + "cheerio": "~0.13.1", + "gulp-util": "~2.2.14", + "lodash": "^3.10.1", + "through2": "~0.4.1" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "dev": true + }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "dev": true + }, + "chalk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "dev": true, + "requires": { + "ansi-styles": "^1.1.0", + "escape-string-regexp": "^1.0.0", + "has-ansi": "^0.1.0", + "strip-ansi": "^0.3.0", + "supports-color": "^0.2.0" + } + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "gulp-util": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", + "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", + "dev": true, + "requires": { + "chalk": "^0.5.0", + "dateformat": "^1.0.7-1.2.3", + "lodash._reinterpolate": "^2.4.1", + "lodash.template": "^2.4.1", + "minimist": "^0.2.0", + "multipipe": "^0.1.0", + "through2": "^0.5.0", + "vinyl": "^0.2.1" + }, + "dependencies": { + "through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "dev": true, + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~3.0.0" + } + } + } + }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "dev": true, + "requires": { + "ansi-regex": "^0.2.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", + "integrity": "sha1-TxInqlqHEfxjL1sHofRgequLMiI=", + "dev": true + }, + "lodash.escape": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", + "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", + "dev": true, + "requires": { + "lodash._escapehtmlchar": "~2.4.1", + "lodash._reunescapedhtml": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "lodash.template": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", + "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", + "dev": true, + "requires": { + "lodash._escapestringchar": "~2.4.1", + "lodash._reinterpolate": "~2.4.1", + "lodash.defaults": "~2.4.1", + "lodash.escape": "~2.4.1", + "lodash.keys": "~2.4.1", + "lodash.templatesettings": "~2.4.1", + "lodash.values": "~2.4.1" + } + }, + "lodash.templatesettings": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", + "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", + "dev": true, + "requires": { + "lodash._reinterpolate": "~2.4.1", + "lodash.escape": "~2.4.1" + } + }, + "minimist": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", + "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=", + "dev": true + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "dev": true, + "requires": { + "ansi-regex": "^0.2.1" + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "dev": true + }, + "through2": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", + "dev": true, + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~2.1.1" + }, + "dependencies": { + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dev": true, + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, + "vinyl": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", + "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", + "dev": true, + "requires": { + "clone-stats": "~0.0.1" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "dev": true + } + } + }, + "gulp-uglify": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-3.0.2.tgz", + "integrity": "sha512-gk1dhB74AkV2kzqPMQBLA3jPoIAPd/nlNzP2XMDSG8XZrqnlCiDGAqC+rZOumzFvB5zOphlFh6yr3lgcAb/OOg==", + "dev": true, + "requires": { + "array-each": "^1.0.1", + "extend-shallow": "^3.0.2", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "isobject": "^3.0.1", + "make-error-cause": "^1.1.1", + "safe-buffer": "^5.1.2", + "through2": "^2.0.0", + "uglify-js": "^3.0.5", + "vinyl-sourcemaps-apply": "^0.2.0" + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", + "replace-ext": "0.0.1", + "through2": "^2.0.0", + "vinyl": "^0.5.0" + }, + "dependencies": { + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulp-zip": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-5.0.0.tgz", + "integrity": "sha512-oR3t8kn+ccHkSyRcBV5kBLPXrhqTh5d6wBAR7r7wqjNQNBhYvOwPedCwlAaGcNl1qSeXNDn6qOk1Qyxvx9Wrow==", + "dev": true, + "requires": { + "get-stream": "^5.1.0", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "vinyl": "^2.1.0", + "yazl": "^2.5.1" + }, + "dependencies": { + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "dev": true + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + } + }, + "htmlparser2": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.4.0.tgz", + "integrity": "sha1-oc1l9YI60oXhnWOwha1yLQpR6uc=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.2", + "domutils": "1.3", + "readable-stream": "1.1" + }, + "dependencies": { + "domutils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.3.0.tgz", + "integrity": "sha1-mtTVm1r2ymhMYv5tdo7xcOcN8ZI=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.1.tgz", + "integrity": "sha512-CKstxrctq1kUesU6WhtZDbYKzzYBuRH0UYInAVrkc/EYdB9ltbfE0gOoayG9nhohG6447sOOVGhHqsdmBvkbNg==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "istextorbinary": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", + "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", + "dev": true, + "requires": { + "binaryextensions": "2", + "editions": "^1.3.3", + "textextensions": "2" + } + }, + "jsdelivr-cdn-data": { + "version": "git://github.com/shahata/jsdelivr-cdn-data.git#d014a2ad1bdfb4c6e3d3cefc7f264435281b91e0", + "from": "git://github.com/shahata/jsdelivr-cdn-data.git#d014a2ad1bdfb4c6e3d3cefc7f264435281b91e0", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "jshint": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.10.2.tgz", + "integrity": "sha512-e7KZgCSXMJxznE/4WULzybCMNXNAd/bf5TSrvVEq78Q/K8ZwFpmBqQeDtNiHc3l49nV4E/+YeHU/JZjSUIrLAA==", + "dev": true, + "requires": { + "cli": "~1.0.0", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "lodash": "~4.17.11", + "minimatch": "~3.0.2", + "shelljs": "0.3.x", + "strip-json-comments": "1.0.x" + }, + "dependencies": { + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", + "dev": true, + "requires": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + } + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "^2.0.5" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "dev": true, + "requires": { + "flush-write-stream": "^1.0.2" + } + }, + "liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "requires": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._escapehtmlchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", + "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", + "dev": true, + "requires": { + "lodash._htmlescapes": "~2.4.1" + } + }, + "lodash._escapestringchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", + "integrity": "sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._htmlescapes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", + "integrity": "sha1-MtFL8IRLbeb4tioFG09nwii2JMs=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=", + "dev": true + }, + "lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._reunescapedhtml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", + "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", + "dev": true, + "requires": { + "lodash._htmlescapes": "~2.4.1", + "lodash.keys": "~2.4.1" + }, + "dependencies": { + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + } + } + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.defaults": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1", + "lodash.keys": "~2.4.1" + }, + "dependencies": { + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + } + } + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "^3.0.0" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" + } + }, + "lodash.values": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", + "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", + "dev": true, + "requires": { + "lodash.keys": "~2.4.1" + }, + "dependencies": { + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + } + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "make-error-cause": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", + "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", + "dev": true, + "requires": { + "make-error": "^1.2.0" + } + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "dev": true, + "requires": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + } + } + }, + "merge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.4.tgz", + "integrity": "sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + } + }, + "mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "ng-annotate": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ng-annotate/-/ng-annotate-1.2.2.tgz", + "integrity": "sha1-3D/FG6Cy+LOF2+BH9NoG9YCh/WE=", + "dev": true, + "requires": { + "acorn": "~2.6.4", + "alter": "~0.2.0", + "convert-source-map": "~1.1.2", + "optimist": "~0.6.1", + "ordered-ast-traverse": "~1.1.1", + "simple-fmt": "~0.1.0", + "simple-is": "~0.2.0", + "source-map": "~0.5.3", + "stable": "~0.1.5", + "stringmap": "~0.2.2", + "stringset": "~0.2.1", + "tryor": "~0.1.2" + }, + "dependencies": { + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + } + } + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "requires": { + "once": "^1.3.2" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } + } + }, + "ordered-ast-traverse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ordered-ast-traverse/-/ordered-ast-traverse-1.1.1.tgz", + "integrity": "sha1-aEOhcLwO7otSDMjdwd3TqjD6BXw=", + "dev": true, + "requires": { + "ordered-esprima-props": "~1.1.0" + } + }, + "ordered-esprima-props": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ordered-esprima-props/-/ordered-esprima-props-1.1.0.tgz", + "integrity": "sha1-qYJwht9fAQqmDpvQK24DNc6i/8s=", + "dev": true + }, + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", + "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "rcfinder": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/rcfinder/-/rcfinder-0.1.9.tgz", + "integrity": "sha1-8+gPOH3fmugK4wpBADKWQuroERU=", + "dev": true, + "requires": { + "lodash.clonedeep": "^4.3.2" + } + }, + "rcloader": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/rcloader/-/rcloader-0.2.2.tgz", + "integrity": "sha1-WNIpi0YtC5v9ITPSoex0+9cFxxc=", + "dev": true, + "requires": { + "lodash.assign": "^4.2.0", + "lodash.isobject": "^3.0.2", + "lodash.merge": "^4.6.0", + "rcfinder": "^0.1.6" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "dependencies": { + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + } + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "dev": true, + "requires": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + } + }, + "replacestream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", + "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.3", + "object-assign": "^4.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "dev": true, + "requires": { + "value-or-function": "^3.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "dev": true, + "requires": { + "sver-compat": "^1.5.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "simple-fmt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz", + "integrity": "sha1-GRv1ZqWeZTBILLJatTtKjchcOms=", + "dev": true + }, + "simple-is": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz", + "integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "stream-to-array": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz", + "integrity": "sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=", + "dev": true, + "requires": { + "any-promise": "^1.1.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringmap": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz", + "integrity": "sha1-VWwTeyWPlCuHdvWy71gqoGnX0bE=", + "dev": true + }, + "stringset": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz", + "integrity": "sha1-7yWcTjSTRDd/zRyRPdLoSMnAQrU=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "dev": true, + "requires": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "ternary-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz", + "integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==", + "dev": true, + "requires": { + "duplexify": "^4.1.1", + "fork-stream": "^0.0.4", + "merge-stream": "^2.0.0", + "through2": "^3.0.1" + }, + "dependencies": { + "duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "dev": true, + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "textextensions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.5.0.tgz", + "integrity": "sha512-1IkVr355eHcomgK7fgj1Xsokturx6L5S2JRT5WcRdA6v5shk9sxWuO/w/VbpQexwkXJMQIa/j1dBi3oo7+HhcA==", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "dev": true, + "requires": { + "through2": "^2.0.3" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "tryor": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz", + "integrity": "sha1-gUXkynyv9ArN48z5Rui4u3W0Fys=", + "dev": true + }, + "type": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.3.tgz", + "integrity": "sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, + "underscore": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.5.2.tgz", + "integrity": "sha1-EzXF5PXm0zu7SwBrqMhqAPVW3gg=", + "dev": true + }, + "undertaker": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", + "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "dev": true, + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v8flags": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", + "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", + "dev": true + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + }, + "vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "requires": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + } + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "dev": true, + "requires": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + } + }, + "vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "dev": true, + "requires": { + "source-map": "^0.5.1" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + }, + "yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3" + } + } + } +} diff --git a/Grbl_Esp32-master/embedded/package.json b/Grbl_Esp32-master/embedded/package.json new file mode 100644 index 0000000..b7a2d06 --- /dev/null +++ b/Grbl_Esp32-master/embedded/package.json @@ -0,0 +1,29 @@ +{ + "name": "embedded4ESP3D", + "description": "Embedded files for ESP3D", + "devDependencies": { + "del": "latest", + "deprecated": "latest", + "fs": "latest", + "gulp": "^4.0.0", + "gulp-bytediff": "latest", + "gulp-cdnizer": "latest", + "gulp-clean-css": "latest", + "gulp-concat": "latest", + "gulp-gzip": "latest", + "gulp-htmlmin": "latest", + "gulp-if": "latest", + "gulp-jshint": "latest", + "gulp-ng-annotate": "latest", + "gulp-remove-code": "latest", + "gulp-replace": "latest", + "gulp-smoosher": "latest", + "gulp-uglify": "latest", + "gulp-zip": "latest", + "jshint": "latest", + "merge-stream": "latest" + }, + "repository": "https://github.com/luc-github/ESP3D", + "author": "Luc LEBOSSE", + "license": "(ISC OR GPL-3.0)" +} diff --git a/Grbl_Esp32-master/embedded/tool.html.gz b/Grbl_Esp32-master/embedded/tool.html.gz new file mode 100644 index 0000000..1fb223f Binary files /dev/null and b/Grbl_Esp32-master/embedded/tool.html.gz differ diff --git a/Grbl_Esp32-master/embedded/www/css/style.css b/Grbl_Esp32-master/embedded/www/css/style.css new file mode 100644 index 0000000..550ff9e --- /dev/null +++ b/Grbl_Esp32-master/embedded/www/css/style.css @@ -0,0 +1,105 @@ +html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%; font-size:10px;} +body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333333;background-color:#ffffff;} +table{border:0px;border-spacing:0;max-width:100%;} +td{white-space:nowrap; padding:2mm;} +th{text-align:left;} +.table>thead>tr>th,.table>tbody>tr>th,.table>thead>tr>td,.table>tbody>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd;} +.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9;} +a{position:relative;display:block;padding:10px 15px;text-decoration:none;color:#cccccc;} +.active{color:#ffffff;background-color:#000000;} +.active a,a:hover,a:focus{color:#FFFFFF;} +.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05);} +.panel-body{padding:15px;} +.panel-heading{padding:10px 15px;color:#333333;background-color:#f5f5f5;border-color:#dddddd;border-top-right-radius:3px;border-top-left-radius:3px;border-bottom:1px solid #dddddd;} +.form-control{display:block;width:auto;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff +;background-image:none;border:1px solid #cccccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075); +* -webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s; +* transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,0.6); +* box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,0.6);} +.form-group{margin-bottom:15px;} +.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer; +background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px; +* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;} +.btn-primary{color:#ffffff;background-color:#337ab7;border-color:#2e6da4;} +.btn-primary:focus,.btn-primary:active,.btn-primary:hover,.btn-primary.focus,.btn-primary.active,.btn-primary.hover{color:#ffffff;background-color:#286090;border-color:#122b40;} +.btnimg {cursor:hand; border-radius:6px ;border:1px solid #FFFFFF;} +.btnimg:hover{background-color:#F0F0F0;border-color:#6c6c6c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #808080;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #808080;} +.btnroundimg {cursor:hand; border-radius:30px;} +.btnroundimg:hover{background-color:#F0F0F0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #808080;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #808080;} +.blacklink, .blacklink:active {color:#000000;} +.blacklink:hover{color:#000000;} +input[type="file"]::-webkit-file-upload-button{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer; +background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px; +* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #5bc0de;border-color: #46b8da;} +input[type="file"]::-webkit-file-upload-button:focus{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer; +background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px; +* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #1b6d85;} +input[type="file"]::-webkit-file-upload-button:hover{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer; +background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px; +* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #269abc;} +.panel-footer{padding:10px 15px;color:#31708f;background-color:#f5f5f5;border-color:#dddddd;border-top:1px solid #dddddd;} +.panel-footer{padding:10px 15px;color:#31708f;background-color:#f5f5f5;border-color:#dddddd;border-top:1px solid #dddddd;} + +.modal { + display: none; /* Hidden by default */ + position: fixed; /* Stay in place */ + z-index: 10000; /* Sit on top */ + padding-top: 100px; /* Location of the box */ + left: 0; + top: 0; + width: 100%; /* Full width */ + height: 100%; /* Full height */ + overflow: auto; /* Enable scroll if needed */ + background-color: rgb(0,0,0); /* Fallback color */ + background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ +} + +/* Modal Content */ +.modal-content { + border-top-left-radius: 10px; + border-top-right-radius: 10px; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + border: 2px solid #337AB7; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + position: relative; + margin: auto; + padding: 0; + background-color: #fefefe; +} + +.modal-header { + padding: 2px 16px; + color: #0f0f0f; + background-color: #f2f2f2; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + border-bottom: 1px solid #cfcfcf; +} + +.modal-body {padding: 10px 16px;} + +.modal-footer { + padding: 16px 16px; + height: 4.5em; + color: #0f0f0f; + background-color: #f2f2f2; + border-top: 1px solid #cfcfcf; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; +} +.hide_it { + display: none; +} + +@media (min-width: 768px) { + .modal-content { + width: 580px; + } +} + +@media screen and (max-width: 767px) { + .modal-content { + width: 100%; + } +} diff --git a/Grbl_Esp32-master/embedded/www/js/script.js b/Grbl_Esp32-master/embedded/www/js/script.js new file mode 100644 index 0000000..ab226d6 --- /dev/null +++ b/Grbl_Esp32-master/embedded/www/js/script.js @@ -0,0 +1,505 @@ +var currentpath = "/"; +var authentication = false; +var websocket_port = 0; +var websocket_IP = ""; +var async_webcommunication = false; +var page_id = ""; +var ws_source; +var log_off =false; +var websocket_started =false; +var esp_error_message =""; +var esp_error_code = 0; +var xmlhttpupload; +var typeupload = 0; +function navbar(){ + var content=""; + var tlist = currentpath.split("/"); + var path="/"; + var nb = 1; + content+=""; + while (nb < (tlist.length-1)) + { + path+=tlist[nb] + "/"; + content+=""; + nb++; + } + content+="
/"+tlist[nb] +"/
"; + return content; +} + +function trash_icon(){ + var content =""; + content +=""; + content +=""; + content +=""; + content +=""; + content +=""; + content +=""; + content +=""; + content +=""; + return content; +} + +function back_icon(){ + var content =""; + return content; +} + +function select_dir(directoryname){ + currentpath+=directoryname + "/"; + SendCommand('list','all'); +} + +function compareStrings(a, b) { + // case-insensitive comparison + a = a.toLowerCase(); + b = b.toLowerCase(); + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +function dispatchfilestatus(jsonresponse) +{ +var content =""; +var display_message = false; +content ="  Status: "+jsonresponse.status; +content +="  |  Total space: "+jsonresponse.total; +content +="  |  Used space: "+jsonresponse.used; +content +="  |  Occupation: "; +content +=" "+jsonresponse.occupation +"%"; +document.getElementById('status').innerHTML=content; +content =""; +if (currentpath!="/") + { + var pos = currentpath.lastIndexOf("/",currentpath.length-2); + var previouspath = currentpath.slice(0,pos+1); + content +=""+back_icon()+" Up.."; + } +jsonresponse.files.sort(function(a, b) { + return compareStrings(a.name, b.name); +}); +if (currentpath=="/") { + display_message = true; +} +var display_time =false; +for (var i1=0;i1
"; + content +=jsonresponse.files[i1].name; + if ((jsonresponse.files[i1].name == "index.html.gz")||(jsonresponse.files[i1].name == "index.html")){ + display_message = false; + } + content +="
"; + content +=jsonresponse.files[i1].size; + content +=""; + if (jsonresponse.files[i1].hasOwnProperty('time')){ + display_time = true; + content +=""; + content += jsonresponse.files[i1].time; + content +=""; + } else { + content +=""; + } + content +="
"; + content +=trash_icon(); + content +="
"; + } +} +//then display directories +for (var i2=0;i2 "; + content +=jsonresponse.files[i2].name; + content +=""; + if (typeof jsonresponse.files[i2].hasOwnProperty('time')){ + display_time = true; + } + content +="
"; + content +=trash_icon(); + content +="
"; + } +} +if(display_time){ + document.getElementById('FS_time').innerHTML = ""; +} else { + document.getElementById('FS_time').innerHTML = "Time"; +} + if (display_message) { + + document.getElementById('MSG').innerHTML = "File index.html.gz is missing, please upload it"; + } else { + document.getElementById('MSG').innerHTML = "Go to ESP3D interface"; + } + document.getElementById('file_list').innerHTML=content; + document.getElementById('path').innerHTML=navbar();} + +function Delete(filename){ +if (confirm("Confirm deletion of file: " + filename))SendCommand("delete",filename); +} + +function Deletedir(filename){ +if (confirm("Confirm deletion of directory: " + filename))SendCommand("deletedir",filename); +} + +function Createdir(){ +var filename = prompt("Directory name", ""); +if (filename != null) { + SendCommand("createdir",filename.trim()); + } +} + +function SendCommand(action,filename){ +var xmlhttp = new XMLHttpRequest(); +var url = "/files?action="+action; +document.getElementById('MSG').innerHTML = "Connecting..."; +url += "&filename="+encodeURI(filename); +url += "&path="+encodeURI(currentpath); +xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == 4 ) { + if(xmlhttp.status == 200) { + var jsonresponse = JSON.parse(xmlhttp.responseText); + dispatchfilestatus(jsonresponse); + } else { + if(xmlhttp.status == 401) { + RL (); + } else { + console.log(xmlhttp.status); + FWError(); + } + } + } +}; +xmlhttp.open("GET", url, true); +xmlhttp.send(); +} + +function Sendfile(){ +var files = document.getElementById('file-select').files; +if (files.length==0)return; +document.getElementById('upload-button').value = "Uploading..."; +document.getElementById('prg').style.visibility = "visible"; +var formData = new FormData(); +formData.append('path', currentpath); +for (var i3 = 0; i3 < files.length; i3++) { +var file = files[i3]; +var arg = currentpath + file.name + "S"; + //append file size first to check updload is complete + formData.append(arg, file.size); + formData.append('myfiles[]', file, currentpath+file.name);} +xmlhttpupload = new XMLHttpRequest(); +xmlhttpupload.open('POST', '/files', true); +//progress upload event +xmlhttpupload.upload.addEventListener("progress", updateProgress, false); +//progress function +function updateProgress (oEvent) { + if (oEvent.lengthComputable) { + var percentComplete = (oEvent.loaded / oEvent.total)*100; + document.getElementById('prg').value=percentComplete; + document.getElementById('upload-button').value = "Uploading ..." + percentComplete.toFixed(0)+"%" ; + } else { + // Impossible because size is unknown + } +} +typeupload = 1; +xmlhttpupload.onload = function () { + if (xmlhttpupload.status === 200) { +document.getElementById('upload-button').value = 'Upload'; +document.getElementById('prg').style.visibility = "hidden"; +document.getElementById('file-select').value=""; +var jsonresponse = JSON.parse(xmlhttpupload.responseText); +dispatchfilestatus(jsonresponse); + } else uploadError(); +}; + +xmlhttpupload.send(formData); +} + +function padNumber(num, size) { + var s = num.toString(); + while (s.length < size) s = "0" + s; + return s; +} +function getPCTime(){ + var d = new Date(); + return d.getFullYear() + "-" + padNumber(d.getMonth() + 1 ,2) + "-" + padNumber(d.getDate(),2) + "-" + padNumber(d.getHours(),2) + "-" + padNumber(d.getMinutes(),2) + "-" + padNumber(d.getSeconds(),2); +} + +function HideAll(msg){ + //console.log("Hide all:" + msg); + log_off = true; + if(websocket_started){ + ws_source.close(); + } + document.title = document.title + "(disconnected)"; + document.getElementById('MSG').innerHTML = msg; + document.getElementById('FILESYSTEM').style.display = "none"; + document.getElementById('FWUPDATE').style.display = "none"; +} + +function FWError(){ + HideAll("Failed to communicate with FW!"); +} + +function FWOk(){ + document.getElementById('MSG').innerHTML = "Connected"; + document.getElementById('FILESYSTEM').style.display = "block"; + document.getElementById('FWUPDATE').style.display = "block"; +} + +function InitUI(){ +var xmlhttp = new XMLHttpRequest(); +var url = "/command?commandText="+encodeURI("[ESP800]"); +authentication = false; +async_webcommunication = false; +console.log("Init UI"); +xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == 4 ) { + var error = false; + if(xmlhttp.status == 200) { + var response = xmlhttp.responseText; + var nbitem = 0; + var tresp = response.split("#"); + console.log(xmlhttp.responseText); + if (tresp.length < 3) { + error = true; + } else { + //FW version:1.1f # FW target:grbl-embedded # FW HW:Direct SD # primary sd:/sd # secondary sd:none # authentication:no # webcommunication: Sync: 81:192.168.0.1 # hostname:grblesp # axis:3 + for (var p=0; p < tresp.length; p++){ + var sublist = tresp[p].split(":"); + if (sublist[0].trim() == "FW version"){ + document.getElementById('FWVERSION').innerHTML = "v"+sublist[1]; + nbitem++; + } + if (sublist[0].trim() == "authentication"){ + if (sublist[1].trim() == "no") { + authentication = false; + document.getElementById('loginicon').style.visibility = "hidden"; + } + else { + authentication = true; + document.getElementById('loginicon').style.visibility = "visible"; + } + nbitem++; + } + if (sublist[0].trim() == "webcommunication"){ + websocket_port = sublist[2].trim(); + websocket_IP = sublist[3].trim(); + startSocket(); + nbitem++; + } + if (sublist[0].trim() == "hostname"){ + document.title = sublist[1].trim(); + nbitem++ + } + } + if (nbitem == 4) { + SendCommand('list','all'); + FWOk(); + } else { + error = true; + + } + } + + + + } else if (xmlhttp.status == 401){ + RL(); + } else { + error = true; + console.log( xmlhttp.status); + } + if (error) { + FWError(); + } + } +}; +xmlhttp.open("GET", url, true); +xmlhttp.send(); +} + +function startSocket(){ + if (websocket_started){ + ws_source.close(); + } + if(async_webcommunication){ + ws_source = new WebSocket('ws://'+websocket_IP + ':' + websocket_port +'/ws',['arduino']); + } + else { + //console.log("Socket port is :" + websocket_port); + ws_source = new WebSocket('ws://'+websocket_IP + ':' + websocket_port,['arduino']); + } + ws_source.binaryType = "arraybuffer"; + ws_source.onopen = function(e){ + console.log("WS"); + websocket_started = true; + }; + ws_source.onclose = function(e){ + websocket_started = false; + console.log("~WS"); + //seems sometimes it disconnect so wait 3s and reconnect + //if it is not a log off + if(!log_off) setTimeout(startSocket, 3000); + }; + ws_source.onerror = function(e){ + console.log("WS", e); + }; + ws_source.onmessage = function(e){ + var msg = ""; + //bin + if(!(e.data instanceof ArrayBuffer)){ + msg = e.data; + var tval = msg.split(":"); + if (tval.length >= 2) { + if (tval[0] == 'currentID') { + page_id = tval[1]; + console.log("ID " + page_id); + } + if (tval[0] == 'activeID') { + if(page_id != tval[1]) { + HideAll("It seems you are connect from another location, your are now disconnected"); + } + } + if (tval[0] == 'ERROR') { + esp_error_message = tval[2]; + esp_error_code = tval[1]; + console.log(tval[2] + " code:" + tval[1]); + uploadError(); + xmlhttpupload.abort(); + } + } + + } + //console.log(msg); + + }; +} + +window.onload = function() { +InitUI(); +}; + +function uploadError() +{ + if (esp_error_code != 0) { + alert('Update failed(' + esp_error_code + '): ' + esp_error_message); + esp_error_code = 0; + } else { + alert('Update failed!'); + } + + if (typeupload == 1) { + //location.reload(); + document.getElementById('upload-button').value = 'Upload'; + document.getElementById('prg').style.visibility = "hidden"; + document.getElementById('file-select').value=""; + SendCommand('list', 'all'); + } else { + location.reload(); + } +} + +function Uploadfile(){ +if (!confirm("Confirm Firmware Update ?"))return; +var files = document.getElementById('fw-select').files; +if (files.length==0)return; +document.getElementById('ubut').style.visibility = 'hidden'; +document.getElementById('fw-select').style.visibility = 'hidden'; +document.getElementById('msg').style.visibility = "visible"; +document.getElementById('msg').innerHTML=""; +document.getElementById('FILESYSTEM').style.display = "none"; +document.getElementById('prgfw').style.visibility = "visible"; +var formData = new FormData(); +for (var i4 = 0; i4 < files.length; i4++) { +var file = files[i4]; +var arg = "/" + file.name + "S"; + //append file size first to check updload is complete + formData.append(arg, file.size); + formData.append('myfile[]', file, "/"+file.name);} +typeupload = 0; +xmlhttpupload = new XMLHttpRequest(); +xmlhttpupload.open('POST', '/updatefw', true); +//progress upload event +xmlhttpupload.addEventListener("progress", updateProgress, false); +//progress function +function updateProgress (oEvent) { + if (oEvent.lengthComputable) { + var percentComplete = (oEvent.loaded / oEvent.total)*100; + document.getElementById('prgfw').value=percentComplete; + document.getElementById('msg').innerHTML = "Uploading ..." + percentComplete.toFixed(0)+"%" ; + } else { + // Impossible because size is unknown + } +} +xmlhttpupload.onload = function () { + if (xmlhttpupload.status === 200) { +document.getElementById('ubut').value = 'Upload'; +document.getElementById('msg').innerHTML="Restarting, please wait...."; +document.getElementById('counter').style.visibility = "visible"; +document.getElementById('ubut').style.visibility = 'hidden'; +document.getElementById('ubut').style.width = '0px'; +document.getElementById('fw-select').value=""; +document.getElementById('fw-select').style.visibility = 'hidden'; +document.getElementById('fw-select').style.width = '0px'; + +var jsonresponse = JSON.parse(xmlhttpupload.responseText); +if (jsonresponse.status=='1' || jsonresponse.status=='4' || jsonresponse.status=='1')uploadError(); +if (jsonresponse.status=='2')alert('Update canceled!'); +else if (jsonresponse.status=='3') +{ + var i5 = 0; + var interval; + var x = document.getElementById("prgfw"); + x.max=40; + interval = setInterval(function(){ + i5=i5+1; + var x = document.getElementById("prgfw"); + x.value=i5; + document.getElementById('counter').innerHTML=41-i5; + if (i5>40) + { + clearInterval(interval); + location.reload(); + } + },1000); +} +else uploadError() + } else uploadError() +}; +xmlhttpupload.send(formData); +} + + +function RL(){ + document.getElementById('loginpage').style.display='block'; +} + +function SLR (){ + document.getElementById('loginpage').style.display='none'; + var user = document.getElementById('lut').value.trim(); + var password = document.getElementById('lpt').value.trim(); + var url = "/login?USER="+encodeURIComponent(user) + "&PASSWORD=" + encodeURIComponent(password) + "&SUBMIT=yes" ; + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == 4){ + if (xmlhttp.status != 200) { + if (xmlhttp.status == 401) { + RL(); + } else { + FWError(); + console.log(xmlhttp.status); + } + } else { + InitUI(); + } + } + }; +xmlhttp.open("GET", url, true); +xmlhttp.send(); +} diff --git a/Grbl_Esp32-master/embedded/www/tool.html b/Grbl_Esp32-master/embedded/www/tool.html new file mode 100644 index 0000000..fea90df --- /dev/null +++ b/Grbl_Esp32-master/embedded/www/tool.html @@ -0,0 +1,142 @@ + + + + + + ESP3D tool page + + + +
+

+
+
+
Flash Filesystem
+
+ +    +

+
+
+ + + + + + +
+ + +
+ + +
+
+
 
+
+ + + + + + + + + + + + +
TypeNameSizeTime
+
+ +
+
+
+
+
Firmware Update
+
+ + + + + + + +
+
+
+ + + + + + + + diff --git a/Grbl_Esp32-master/libraries/ESP32SSDP/ESP32SSDP.cpp b/Grbl_Esp32-master/libraries/ESP32SSDP/ESP32SSDP.cpp new file mode 100644 index 0000000..59debf5 --- /dev/null +++ b/Grbl_Esp32-master/libraries/ESP32SSDP/ESP32SSDP.cpp @@ -0,0 +1,464 @@ +/* +ESP32 Simple Service Discovery +Copyright (c) 2015 Hristo Gochkov + +Original (Arduino) version by Filippo Sallemi, July 23, 2014. +Can be found at: https://github.com/nomadnt/uSSDP + +License (MIT license): + 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. + +*/ + +#include +#include "ESP32SSDP.h" +#include "WiFiUdp.h" +#include + +//#define DEBUG_SSDP Serial + +#define SSDP_INTERVAL 1200 +#define SSDP_PORT 1900 +#define SSDP_METHOD_SIZE 10 +#define SSDP_URI_SIZE 2 +#define SSDP_BUFFER_SIZE 64 +#define SSDP_MULTICAST_TTL 2 +static const IPAddress SSDP_MULTICAST_ADDR(239, 255, 255, 250); + + + +static const char _ssdp_response_template[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "EXT:\r\n"; + +static const char _ssdp_notify_template[] PROGMEM = + "NOTIFY * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "NTS: ssdp:alive\r\n"; + +static const char _ssdp_packet_template[] PROGMEM = + "%s" // _ssdp_response_template / _ssdp_notify_template + "CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL + "SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber + "USN: uuid:%s\r\n" // _uuid + "%s: %s\r\n" // "NT" or "ST", _deviceType + "LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL + "\r\n"; + +static const char _ssdp_schema_template[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/xml\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "\r\n" + "" + "" + "" + "1" + "0" + "" + "http://%u.%u.%u.%u:%u/" // WiFi.localIP(), _port + "" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "uuid:%s" + "" +// "" +// "" +// "image/png" +// "48" +// "48" +// "24" +// "icon48.png" +// "" +// "" +// "image/png" +// "120" +// "120" +// "24" +// "icon120.png" +// "" +// "" + "\r\n" + "\r\n"; + +struct SSDPTimer { + ETSTimer timer; +}; + +SSDPClass::SSDPClass() : +_server(0), +_timer(0), +_port(80), +_ttl(SSDP_MULTICAST_TTL), +_respondToPort(0), +_pending(false), +_delay(0), +_process_time(0), +_notify_time(0) +{ + _uuid[0] = '\0'; + _modelNumber[0] = '\0'; + sprintf(_deviceType, "urn:schemas-upnp-org:device:Basic:1"); + _friendlyName[0] = '\0'; + _presentationURL[0] = '\0'; + _serialNumber[0] = '\0'; + _modelName[0] = '\0'; + _modelURL[0] = '\0'; + _manufacturer[0] = '\0'; + _manufacturerURL[0] = '\0'; + sprintf(_schemaURL, "ssdp/schema.xml"); +} + +SSDPClass::~SSDPClass(){ + end(); +} + +void SSDPClass::end(){ + if(!_server) { + return; + } +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf_P(PSTR("SSDP end ... ")); +#endif + // undo all initializations done in begin(), in reverse order + _stopTimer(); + _server->stop(); + delete (_server); + _server = 0; +} + + +bool SSDPClass::begin(){ + _pending = false; + end(); + uint32_t chipId = ((uint16_t) (ESP.getEfuseMac() >> 32)); + sprintf(_uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x", + (uint16_t) ((chipId >> 16) & 0xff), + (uint16_t) ((chipId >> 8) & 0xff), + (uint16_t) chipId & 0xff ); + assert(nullptr == _server); + _server = new WiFiUDP; +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid); +#endif + + + _server = new WiFiUDP; + if (!(_server->beginMulticast(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT))) { +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Error begin"); +#endif + return false; + } + + _startTimer(); + + return true; +} + +void SSDPClass::_send(ssdp_method_t method){ + char buffer[1460]; + IPAddress ip = WiFi.localIP(); + + char valueBuffer[strlen_P(_ssdp_notify_template)+1]; + strcpy_P(valueBuffer, (method == NONE)?_ssdp_response_template:_ssdp_notify_template); + + int len = snprintf_P(buffer, sizeof(buffer), + _ssdp_packet_template, + valueBuffer, + SSDP_INTERVAL, + _modelName, _modelNumber, + _uuid, + (method == NONE)?"ST":"NT", + _deviceType, + ip[0], ip[1], ip[2], ip[3], _port, _schemaURL + ); + if(len < 0) return; + IPAddress remoteAddr; + uint16_t remotePort; + if(method == NONE) { + remoteAddr = _respondToAddr; + remotePort = _respondToPort; +#ifdef DEBUG_SSDP + DEBUG_SSDP.print("Sending Response to "); +#endif + } else { + remoteAddr = IPAddress(SSDP_MULTICAST_ADDR); + remotePort = SSDP_PORT; +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Sending Notify to "); +#endif + } +#ifdef DEBUG_SSDP + DEBUG_SSDP.print(remoteAddr); + DEBUG_SSDP.print(":"); + DEBUG_SSDP.println(remotePort); +#endif + _server->beginPacket(remoteAddr, remotePort); + _server->println(buffer); + _server->endPacket(); +} + +void SSDPClass::schema(WiFiClient client){ + IPAddress ip = WiFi.localIP(); + char buffer[strlen_P(_ssdp_schema_template)+1]; + strcpy_P(buffer, _ssdp_schema_template); + client.printf(buffer, + ip[0], ip[1], ip[2], ip[3], _port, + _deviceType, + _friendlyName, + _presentationURL, + _serialNumber, + _modelName, + _modelNumber, + _modelURL, + _manufacturer, + _manufacturerURL, + _uuid + ); +} + +void SSDPClass::_update(){ + int nbBytes =0; + char * packetBuffer = nullptr; + + if(!_pending && _server) { + ssdp_method_t method = NONE; + nbBytes= _server->parsePacket(); + typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states; + states state = METHOD; + typedef enum {START, MAN, ST, MX} headers; + headers header = START; + + uint8_t cursor = 0; + uint8_t cr = 0; + + char buffer[SSDP_BUFFER_SIZE] = {0}; + packetBuffer = new char[nbBytes +1]; + int message_size=_server->read(packetBuffer,nbBytes); + int process_pos = 0; + packetBuffer[message_size]='\0'; + _respondToAddr = _server->remoteIP(); + _respondToPort = _server->remotePort(); +#ifdef DEBUG_SSDP + if (message_size) { + DEBUG_SSDP.println("****************************************************"); + DEBUG_SSDP.println(_server->remoteIP()); + DEBUG_SSDP.println(packetBuffer); + DEBUG_SSDP.println("****************************************************"); + } +#endif + while(process_pos < message_size){ + + char c = packetBuffer[process_pos]; + process_pos++; + (c == '\r' || c == '\n') ? cr++ : cr = 0; +#ifdef DEBUG_SSDP + if ((c == '\r' || c == '\n') && (cr < 2)) DEBUG_SSDP.println(buffer); +#endif + switch(state){ + case METHOD: + if(c == ' '){ + if(strcmp(buffer, "M-SEARCH") == 0) method = SEARCH; + + if(method == NONE) state = ABORT; + else state = URI; + cursor = 0; + + } else if(cursor < SSDP_METHOD_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + break; + case URI: + if(c == ' '){ + if(strcmp(buffer, "*")) state = ABORT; + else state = PROTO; + cursor = 0; + } else if(cursor < SSDP_URI_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + break; + case PROTO: + if(cr == 2){ state = KEY; cursor = 0; } + break; + case KEY: + if(cr == 4){ _pending = true; _process_time = millis(); } + else if(c == ' '){ cursor = 0; state = VALUE; } + else if(c != '\r' && c != '\n' && c != ':' && cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + break; + case VALUE: + if(cr == 2){ + switch(header){ + case START: + break; + case MAN: +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer); +#endif + break; + case ST: + if(strcmp(buffer, "ssdp:all")){ + state = ABORT; +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf("REJECT: %s\n", (char *)buffer); +#endif + } + // if the search type matches our type, we should respond instead of ABORT + if(strcasecmp(buffer, _deviceType) == 0){ + _pending = true; + _process_time = 0; +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("the search type matches our type"); +#endif + state = KEY; + } + break; + case MX: + _delay = random(0, atoi(buffer)) * 1000L; + break; + } + + if(state != ABORT){ state = KEY; header = START; cursor = 0; } + } else if(c != '\r' && c != '\n'){ + if(header == START){ + if(strncmp(buffer, "MA", 2) == 0) header = MAN; + else if(strcmp(buffer, "ST") == 0) header = ST; + else if(strcmp(buffer, "MX") == 0) header = MX; + } + + if(cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + } + break; + case ABORT: + _pending = false; _delay = 0; + break; + } + } + } + if(packetBuffer) delete packetBuffer; + if(_pending && (millis() - _process_time) > _delay){ + _pending = false; _delay = 0; +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Send None"); +#endif + _send(NONE); + } else if(_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)){ + _notify_time = millis(); + #ifdef DEBUG_SSDP + DEBUG_SSDP.println("Send Notify"); +#endif + _send(NOTIFY); + } else { +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Do not sent"); +#endif + } + + if (_pending) { + _server->flush(); + } + +} + +void SSDPClass::setSchemaURL(const char *url){ + strlcpy(_schemaURL, url, sizeof(_schemaURL)); +} + +void SSDPClass::setHTTPPort(uint16_t port){ + _port = port; +} + +void SSDPClass::setDeviceType(const char *deviceType){ + strlcpy(_deviceType, deviceType, sizeof(_deviceType)); +} + +void SSDPClass::setName(const char *name){ + strlcpy(_friendlyName, name, sizeof(_friendlyName)); +} + +void SSDPClass::setURL(const char *url){ + strlcpy(_presentationURL, url, sizeof(_presentationURL)); +} + +void SSDPClass::setSerialNumber(const char *serialNumber){ + strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber)); +} + +void SSDPClass::setSerialNumber(const uint32_t serialNumber){ + snprintf(_serialNumber, sizeof(uint32_t)*2+1, "%08X", serialNumber); +} + +void SSDPClass::setModelName(const char *name){ + strlcpy(_modelName, name, sizeof(_modelName)); +} + +void SSDPClass::setModelNumber(const char *num){ + strlcpy(_modelNumber, num, sizeof(_modelNumber)); +} + +void SSDPClass::setModelURL(const char *url){ + strlcpy(_modelURL, url, sizeof(_modelURL)); +} + +void SSDPClass::setManufacturer(const char *name){ + strlcpy(_manufacturer, name, sizeof(_manufacturer)); +} + +void SSDPClass::setManufacturerURL(const char *url){ + strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL)); +} + +void SSDPClass::setTTL(const uint8_t ttl){ + _ttl = ttl; +} + +void SSDPClass::_onTimerStatic(SSDPClass* self) { +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Update"); +#endif + self->_update(); +} + +void SSDPClass::_startTimer() { + _stopTimer(); + _timer= new SSDPTimer(); + ETSTimer* tm = &(_timer->timer); + const int interval = 1000; + ets_timer_disarm(tm); + ets_timer_setfn(tm, reinterpret_cast(&SSDPClass::_onTimerStatic), reinterpret_cast(this)); + ets_timer_arm(tm, interval, 1 /* repeat */); +} + +void SSDPClass::_stopTimer() { + if(!_timer){ + return; + } + ETSTimer* tm = &(_timer->timer); + ets_timer_disarm(tm); + delete _timer; + _timer = nullptr; +} + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP) +SSDPClass SSDP; +#endif diff --git a/Grbl_Esp32-master/libraries/ESP32SSDP/ESP32SSDP.h b/Grbl_Esp32-master/libraries/ESP32SSDP/ESP32SSDP.h new file mode 100644 index 0000000..bf3b2e2 --- /dev/null +++ b/Grbl_Esp32-master/libraries/ESP32SSDP/ESP32SSDP.h @@ -0,0 +1,128 @@ +/* +ESP32 Simple Service Discovery +Copyright (c) 2015 Hristo Gochkov + +Original (Arduino) version by Filippo Sallemi, July 23, 2014. +Can be found at: https://github.com/nomadnt/uSSDP + +License (MIT license): + 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. + +*/ + +#ifndef ESP32SSDP_H +#define ESP32SSDP_H + +#include +#include +#include + +#define SSDP_UUID_SIZE 37 +#define SSDP_SCHEMA_URL_SIZE 64 +#define SSDP_DEVICE_TYPE_SIZE 64 +#define SSDP_FRIENDLY_NAME_SIZE 64 +#define SSDP_SERIAL_NUMBER_SIZE 32 +#define SSDP_PRESENTATION_URL_SIZE 128 +#define SSDP_MODEL_NAME_SIZE 64 +#define SSDP_MODEL_URL_SIZE 128 +#define SSDP_MODEL_VERSION_SIZE 32 +#define SSDP_MANUFACTURER_SIZE 64 +#define SSDP_MANUFACTURER_URL_SIZE 128 + +typedef enum { + NONE, + SEARCH, + NOTIFY +} ssdp_method_t; + + +struct SSDPTimer; + +class SSDPClass{ + public: + SSDPClass(); + ~SSDPClass(); + + bool begin(); + void end(); + + void schema(WiFiClient client); + + void setDeviceType(const String& deviceType) { setDeviceType(deviceType.c_str()); } + void setDeviceType(const char *deviceType); + void setName(const String& name) { setName(name.c_str()); } + void setName(const char *name); + void setURL(const String& url) { setURL(url.c_str()); } + void setURL(const char *url); + void setSchemaURL(const String& url) { setSchemaURL(url.c_str()); } + void setSchemaURL(const char *url); + void setSerialNumber(const String& serialNumber) { setSerialNumber(serialNumber.c_str()); } + void setSerialNumber(const char *serialNumber); + void setSerialNumber(const uint32_t serialNumber); + void setModelName(const String& name) { setModelName(name.c_str()); } + void setModelName(const char *name); + void setModelNumber(const String& num) { setModelNumber(num.c_str()); } + void setModelNumber(const char *num); + void setModelURL(const String& url) { setModelURL(url.c_str()); } + void setModelURL(const char *url); + void setManufacturer(const String& name) { setManufacturer(name.c_str()); } + void setManufacturer(const char *name); + void setManufacturerURL(const String& url) { setManufacturerURL(url.c_str()); } + void setManufacturerURL(const char *url); + void setHTTPPort(uint16_t port); + void setTTL(uint8_t ttl); + + protected: + void _send(ssdp_method_t method); + void _update(); + void _startTimer(); + void _stopTimer(); + static void _onTimerStatic(SSDPClass* self); + + WiFiUDP *_server; + SSDPTimer* _timer; + uint16_t _port; + uint8_t _ttl; + + IPAddress _respondToAddr; + uint16_t _respondToPort; + + bool _pending; + unsigned short _delay; + unsigned long _process_time; + unsigned long _notify_time; + + char _schemaURL[SSDP_SCHEMA_URL_SIZE]; + char _uuid[SSDP_UUID_SIZE]; + char _deviceType[SSDP_DEVICE_TYPE_SIZE]; + char _friendlyName[SSDP_FRIENDLY_NAME_SIZE]; + char _serialNumber[SSDP_SERIAL_NUMBER_SIZE]; + char _presentationURL[SSDP_PRESENTATION_URL_SIZE]; + char _manufacturer[SSDP_MANUFACTURER_SIZE]; + char _manufacturerURL[SSDP_MANUFACTURER_URL_SIZE]; + char _modelName[SSDP_MODEL_NAME_SIZE]; + char _modelURL[SSDP_MODEL_URL_SIZE]; + char _modelNumber[SSDP_MODEL_VERSION_SIZE]; +}; + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP) +extern SSDPClass SSDP; +#endif + +#endif diff --git a/Grbl_Esp32-master/libraries/ESP32SSDP/README.rst b/Grbl_Esp32-master/libraries/ESP32SSDP/README.rst new file mode 100644 index 0000000..b2c92c7 --- /dev/null +++ b/Grbl_Esp32-master/libraries/ESP32SSDP/README.rst @@ -0,0 +1,22 @@ +ESP32 Simple Service Discovery Copyright (c) 2015 Hristo Gochkov +Original (Arduino) version by Filippo Sallemi, July 23, 2014. Can be +found at: https://github.com/nomadnt/uSSDP + +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. diff --git a/Grbl_Esp32-master/libraries/ESP32SSDP/examples/SSDP/SSDP.ino b/Grbl_Esp32-master/libraries/ESP32SSDP/examples/SSDP/SSDP.ino new file mode 100644 index 0000000..46f0243 --- /dev/null +++ b/Grbl_Esp32-master/libraries/ESP32SSDP/examples/SSDP/SSDP.ino @@ -0,0 +1,52 @@ +#include +#include +#include + +const char* ssid = "********"; +const char* password = "********"; + +WebServer HTTP(80); + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println("Starting WiFi..."); + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if(WiFi.waitForConnectResult() == WL_CONNECTED){ + + Serial.printf("Starting HTTP...\n"); + HTTP.on("/index.html", HTTP_GET, [](){ + HTTP.send(200, "text/plain", "Hello World!"); + }); + HTTP.on("/description.xml", HTTP_GET, [](){ + SSDP.schema(HTTP.client()); + }); + HTTP.begin(); + + Serial.printf("Starting SSDP...\n"); + SSDP.setSchemaURL("description.xml"); + SSDP.setHTTPPort(80); + SSDP.setName("Philips hue clone"); + SSDP.setSerialNumber("001788102201"); + SSDP.setURL("index.html"); + SSDP.setModelName("Philips hue bridge 2012"); + SSDP.setModelNumber("929000226503"); + SSDP.setModelURL("http://www.meethue.com"); + SSDP.setManufacturer("Royal Philips Electronics"); + SSDP.setManufacturerURL("http://www.philips.com"); + SSDP.setDeviceType("upnp:rootdevice"); //to appear as root device + SSDP.begin(); + + Serial.printf("Ready!\n"); + } else { + Serial.printf("WiFi Failed\n"); + while(1) delay(100); + } +} + +void loop() { + HTTP.handleClient(); + delay(1); +} diff --git a/Grbl_Esp32-master/libraries/ESP32SSDP/keywords.txt b/Grbl_Esp32-master/libraries/ESP32SSDP/keywords.txt new file mode 100644 index 0000000..241d341 --- /dev/null +++ b/Grbl_Esp32-master/libraries/ESP32SSDP/keywords.txt @@ -0,0 +1,53 @@ +####################################### +# Syntax Coloring Map For Ultrasound +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +ESP8266SSDP KEYWORD1 +SSDP KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +schema KEYWORD2 +setName KEYWORD2 +setURL KEYWORD2 +setHTTPPort KEYWORD2 +setSchemaURL KEYWORD2 +setSerialNumber KEYWORD2 +setModelName KEYWORD2 +setModelNumber KEYWORD2 +setModelURL KEYWORD2 +setManufacturer KEYWORD2 +setManufacturerURL KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +SSDP_INTERVAL LITERAL1 +SSDP_PORT LITERAL1 +SSDP_METHOD_SIZE LITERAL1 +SSDP_URI_SIZE LITERAL1 +SSDP_BUFFER_SIZE LITERAL1 +SSDP_BASE_SIZE LITERAL1 +SSDP_FRIENDLY_NAME_SIZE LITERAL1 +SSDP_SERIAL_NUMBER_SIZE LITERAL1 +SSDP_PRESENTATION_URL_SIZE LITERAL1 +SSDP_MODEL_NAME_SIZE LITERAL1 +SSDP_MODEL_URL_SIZE LITERAL1 +SSDP_MODEL_VERSION_SIZE LITERAL1 +SSDP_MANUFACTURER_SIZE LITERAL1 +SSDP_MANUFACTURER_URL_SIZE LITERAL1 +SEARCH LITERAL1 +NOTIFY LITERAL1 +BASIC LITERAL1 +MANAGEABLE LITERAL1 +SOLARPROTECTIONBLIND LITERAL1 +DIGITALSECURITYCAMERA LITERAL1 +HVAC LITERAL1 +LIGHTINGCONTROL LITERAL1 diff --git a/Grbl_Esp32-master/libraries/ESP32SSDP/library.properties b/Grbl_Esp32-master/libraries/ESP32SSDP/library.properties new file mode 100644 index 0000000..e37cd25 --- /dev/null +++ b/Grbl_Esp32-master/libraries/ESP32SSDP/library.properties @@ -0,0 +1,9 @@ +name=ESP32SSPD +version=1.0 +author=Me-No-Dev +maintainer=Me-No-Dev +sentence=Simple SSDP library for ESP32 +paragraph=Only for ESP32 +category=Communication +url= +architectures=esp32 diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/.gitignore b/Grbl_Esp32-master/libraries/arduinoWebSockets/.gitignore new file mode 100644 index 0000000..44b2c85 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/.gitignore @@ -0,0 +1,29 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +/tests/webSocketServer/node_modules diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/.travis.yml b/Grbl_Esp32-master/libraries/arduinoWebSockets/.travis.yml new file mode 100644 index 0000000..14693dd --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/.travis.yml @@ -0,0 +1,40 @@ +sudo: false +language: bash +os: + - linux +env: + matrix: + - CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80" IDE_VERSION=1.6.5 + - CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80,FlashSize=1M0,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.5 + - CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80,Debug=Serial1" IDE_VERSION=1.6.5 + - CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.6.5 + - CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.5 + +script: + - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 + - sleep 3 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz + - tar xf arduino-$IDE_VERSION-linux64.tar.xz + - mv arduino-$IDE_VERSION $HOME/arduino_ide + - export PATH="$HOME/arduino_ide:$PATH" + - which arduino + - mkdir -p $HOME/Arduino/libraries + - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/arduinoWebSockets + - source $TRAVIS_BUILD_DIR/travis/common.sh + - get_core $CPU + - cd $TRAVIS_BUILD_DIR + - arduino --board $BOARD --save-prefs + - arduino --get-pref sketchbook.path + - build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets/examples/$CPU $CPU + +notifications: + email: + on_success: change + on_failure: change + webhooks: + urls: + - https://webhooks.gitter.im/e/1aa78fbe15080b0c2e37 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/LICENSE b/Grbl_Esp32-master/libraries/arduinoWebSockets/LICENSE new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/README.md b/Grbl_Esp32-master/libraries/arduinoWebSockets/README.md new file mode 100644 index 0000000..63eef3e --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/README.md @@ -0,0 +1,98 @@ +WebSocket Server and Client for Arduino [![Build Status](https://travis-ci.org/Links2004/arduinoWebSockets.svg?branch=master)](https://travis-ci.org/Links2004/arduinoWebSockets) +=========================================== + +a WebSocket Server and Client for Arduino based on RFC6455. + + +##### Supported features of RFC6455 ##### + - text frame + - binary frame + - connection close + - ping + - pong + - continuation frame + +##### Limitations ##### + - max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define + - max output length has no limit (the hardware is the limit) + - Client send big frames with mask 0x00000000 (on AVR all frames) + - continuation frame reassembly need to be handled in the application code + + ##### Limitations for Async ##### + - Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround. + - wss / SSL is not possible. + +##### Supported Hardware ##### + - ESP8266 [Arduino for ESP8266](https://github.com/esp8266/Arduino/) + - ESP32 [Arduino for ESP32](https://github.com/espressif/arduino-esp32) + - ESP31B + - Particle with STM32 ARM Cortex M3 + - ATmega328 with Ethernet Shield (ATmega branch) + - ATmega328 with enc28j60 (ATmega branch) + - ATmega2560 with Ethernet Shield (ATmega branch) + - ATmega2560 with enc28j60 (ATmega branch) + +###### Note: ###### + + version 2.0 and up is not compatible with AVR/ATmega, check ATmega branch. + + Arduino for AVR not supports std namespace of c++. + +### wss / SSL ### + supported for: + - wss client on the ESP8266 + - wss / SSL is not natively supported in WebSocketsServer however it is possible to achieve secure websockets + by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a + sample Nginx server configuration file to enable this. + +### ESP Async TCP ### + +This libary can run in Async TCP mode on the ESP. + +The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define). + +[ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required. + + +### High Level Client API ### + + - `begin` : Initiate connection sequence to the websocket host. +``` +void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); +void begin(String host, uint16_t port, String url = "/", String protocol = "arduino"); + ``` + - `onEvent`: Callback to handle for websocket events + + ``` + void onEvent(WebSocketClientEvent cbEvent); + ``` + + - `WebSocketClientEvent`: Handler for websocket events + ``` + void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length) + ``` +Where `WStype_t type` is defined as: + ``` + typedef enum { + WStype_ERROR, + WStype_DISCONNECTED, + WStype_CONNECTED, + WStype_TEXT, + WStype_BIN, + WStype_FRAGMENT_TEXT_START, + WStype_FRAGMENT_BIN_START, + WStype_FRAGMENT, + WStype_FRAGMENT_FIN, + } WStype_t; + ``` + +### Issues ### +Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues + +[![Join the chat at https://gitter.im/Links2004/arduinoWebSockets](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Links2004/arduinoWebSockets?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +### License and credits ### + +The library is licensed under [LGPLv2.1](https://github.com/Links2004/arduinoWebSockets/blob/master/LICENSE) + +[libb64](http://libb64.sourceforge.net/) written by Chris Venter. It is distributed under Public Domain see [LICENSE](https://github.com/Links2004/arduinoWebSockets/blob/master/src/libb64/LICENSE). diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf new file mode 100644 index 0000000..ec5aa89 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf @@ -0,0 +1,83 @@ +# ESP8266 nginx SSL reverse proxy configuration file (tested and working on nginx v1.10.0) + +# proxy cache location +proxy_cache_path /opt/etc/nginx/cache levels=1:2 keys_zone=ESP8266_cache:10m max_size=10g inactive=5m use_temp_path=off; + +# webserver proxy +server { + + # general server parameters + listen 50080; + server_name myDomain.net; + access_log /opt/var/log/nginx/myDomain.net.access.log; + + # SSL configuration + ssl on; + ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem; + ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; + ssl_prefer_server_ciphers on; + + location / { + + # proxy caching configuration + proxy_cache ESP8266_cache; + proxy_cache_revalidate on; + proxy_cache_min_uses 1; + proxy_cache_use_stale off; + proxy_cache_lock on; + # proxy_cache_bypass $http_cache_control; + # include the sessionId cookie value as part of the cache key - keeps the cache per user + # proxy_cache_key $proxy_host$request_uri$cookie_sessionId; + + # header pass through configuration + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # ESP8266 custom headers which identify to the device that it's running through an SSL proxy + proxy_set_header X-SSL On; + proxy_set_header X-SSL-WebserverPort 50080; + proxy_set_header X-SSL-WebsocketPort 50081; + + # extra debug headers + add_header X-Proxy-Cache $upstream_cache_status; + add_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # actual proxying configuration + proxy_ssl_session_reuse on; + # target the IP address of the device with proxy_pass + proxy_pass http://192.168.0.20; + proxy_read_timeout 90; + } + } + +# websocket proxy +server { + + # general server parameters + listen 50081; + server_name myDomain.net; + access_log /opt/var/log/nginx/myDomain.net.wss.access.log; + + # SSL configuration + ssl on; + ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem; + ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; + ssl_prefer_server_ciphers on; + + location / { + + # websocket upgrade tunnel configuration + proxy_pass http://192.168.0.20:81; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_read_timeout 86400; + } + } diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino new file mode 100644 index 0000000..9d49d14 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino @@ -0,0 +1,84 @@ +/* + * WebSocketClientAVR.ino + * + * Created on: 10.12.2015 + * + */ + +#include + +#include +#include + +#include + + + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// Set the static IP address to use if the DHCP fails to assign +IPAddress ip(192, 168, 0, 177); + +WebSocketsClient webSocket; + + + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + Serial.println("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + Serial.print("[WSc] Connected to url: "); + Serial.println((char *)payload); + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + Serial.print("[WSc] get text: "); + Serial.println((char *)payload); + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + Serial.print("[WSc] get binary length: "); + Serial.println(length); + // hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() +{ + // Open serial communications and wait for port to open: + Serial.begin(115200); + while (!Serial) {} + + // start the Ethernet connection: + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + // try to congifure using IP address instead of DHCP: + Ethernet.begin(mac, ip); + } + + webSocket.begin("192.168.0.123", 8011); + webSocket.onEvent(webSocketEvent); + +} + + +void loop() +{ + webSocket.loop(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino new file mode 100644 index 0000000..5e5ead4 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino @@ -0,0 +1,110 @@ +/* + * WebSocketClient.ino + * + * Created on: 24.05.2015 + * + */ + +#include + +#include +#include +#include + +#include + + +WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +#define USE_SERIAL Serial1 + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + case WStype_ERROR: + case WStype_FRAGMENT_TEXT_START: + case WStype_FRAGMENT_BIN_START: + case WStype_FRAGMENT: + case WStype_FRAGMENT_FIN: + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // server address, port and URL + webSocket.begin("192.168.0.123", 81, "/"); + + // event handler + webSocket.onEvent(webSocketEvent); + + // use HTTP Basic Authorization this is optional remove if not needed + webSocket.setAuthorization("user", "Password"); + + // try ever 5000 again if connection has failed + webSocket.setReconnectInterval(5000); + +} + +void loop() { + webSocket.loop(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino new file mode 100644 index 0000000..9d72242 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino @@ -0,0 +1,106 @@ +/* + * WebSocketClientSSL.ino + * + * Created on: 10.12.2015 + * + * note SSL is only possible with the ESP8266 + * + */ + +#include + +#include +#include +#include + +#include + + +WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +#define USE_SERIAL Serial1 + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + case WStype_ERROR: + case WStype_FRAGMENT_TEXT_START: + case WStype_FRAGMENT_BIN_START: + case WStype_FRAGMENT: + case WStype_FRAGMENT_FIN: + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.beginSSL("192.168.0.123", 81); + webSocket.onEvent(webSocketEvent); + +} + +void loop() { + webSocket.loop(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino new file mode 100644 index 0000000..3e0d4f5 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino @@ -0,0 +1,104 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include + +#include + +WiFiMulti WiFiMulti; +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial1 + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + case WStype_ERROR: + case WStype_FRAGMENT_TEXT_START: + case WStype_FRAGMENT_BIN_START: + case WStype_FRAGMENT: + case WStype_FRAGMENT_FIN: + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino new file mode 100644 index 0000000..b990c13 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino @@ -0,0 +1,92 @@ +/* + * WebSocketClient.ino + * + * Created on: 24.05.2015 + * + */ + +#include + +#include +#include + +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +#define USE_SERIAL Serial1 + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // server address, port and URL + webSocket.begin("192.168.0.123", 81, "/"); + + // event handler + webSocket.onEvent(webSocketEvent); + + // use HTTP Basic Authorization this is optional remove if not needed + webSocket.setAuthorization("user", "Password"); + + // try ever 5000 again if connection has failed + webSocket.setReconnectInterval(5000); + +} + +void loop() { + webSocket.loop(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino new file mode 100644 index 0000000..d45060e --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino @@ -0,0 +1,88 @@ +/* + * WebSocketClientSSL.ino + * + * Created on: 10.12.2015 + * + * note SSL is only possible with the ESP8266 + * + */ + +#include + +#include +#include + +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + + +#define USE_SERIAL Serial1 + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.beginSSL("192.168.0.123", 81); + webSocket.onEvent(webSocketEvent); + +} + +void loop() { + webSocket.loop(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino new file mode 100644 index 0000000..40e343e --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino @@ -0,0 +1,113 @@ +/* + * WebSocketClientSocketIO.ino + * + * Created on: 06.06.2016 + * + */ + +#include + +#include +#include + +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + + +#define USE_SERIAL Serial1 + +#define MESSAGE_INTERVAL 30000 +#define HEARTBEAT_INTERVAL 25000 + +uint64_t messageTimestamp = 0; +uint64_t heartbeatTimestamp = 0; +bool isConnected = false; + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + isConnected = false; + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + isConnected = true; + + // send message to server when Connected + // socket.io upgrade confirmation message (required) + webSocket.sendTXT("5"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.beginSocketIO("192.168.0.123", 81); + //webSocket.setAuthorization("user", "Password"); // HTTP Basic Authorization + webSocket.onEvent(webSocketEvent); + +} + +void loop() { + webSocket.loop(); + + if(isConnected) { + + uint64_t now = millis(); + + if(now - messageTimestamp > MESSAGE_INTERVAL) { + messageTimestamp = now; + // example socket.io message with type "messageType" and JSON payload + webSocket.sendTXT("42[\"messageType\",{\"greeting\":\"hello\"}]"); + } + if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) { + heartbeatTimestamp = now; + // socket.io heartbeat message + webSocket.sendTXT("2"); + } + } +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino new file mode 100644 index 0000000..a0eb011 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino @@ -0,0 +1,149 @@ +/* + WebSocketClientStomp.ino + + Example for connecting and maintining a connection with a STOMP websocket connection. + In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html). + + Created on: 25.09.2017 + Author: Martin Becker , Contact: becker@informatik.uni-wuerzburg.de +*/ + +// PRE + +#define USE_SERIAL Serial + + +// LIBRARIES + +#include +#include + +#include +#include + + +// SETTINGS + +const char* wlan_ssid = "yourssid"; +const char* wlan_password = "somepassword"; + +const char* ws_host = "the.host.net"; +const int ws_port = 80; + +// URL for STOMP endpoint. +// For the default config of Spring's STOMP support, the default URL is "/socketentry/websocket". +const char* stompUrl = "/socketentry/websocket"; // don't forget the leading "/" !!! + + +// VARIABLES + +WebSocketsClient webSocket; + + +// FUNCTIONS + +/** + * STOMP messages need to be NULL-terminated (i.e., \0 or \u0000). + * However, when we send a String or a char[] array without specifying + * a length, the size of the message payload is derived by strlen() internally, + * thus dropping any NULL values appended to the "msg"-String. + * + * To solve this, we first convert the String to a NULL terminated char[] array + * via "c_str" and set the length of the payload to include the NULL value. + */ +void sendMessage(String & msg) { + webSocket.sendTXT(msg.c_str(), msg.length() + 1); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch (type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + String msg = "CONNECT\r\naccept-version:1.1,1.0\r\nheart-beat:10000,10000\r\n\r\n"; + sendMessage(msg); + } + break; + case WStype_TEXT: + { + // ##################### + // handle STOMP protocol + // ##################### + + String text = (char*) payload; + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + if (text.startsWith("CONNECTED")) { + + // subscribe to some channels + + String msg = "SUBSCRIBE\nid:sub-0\ndestination:/user/queue/messages\n\n"; + sendMessage(msg); + delay(1000); + + // and send a message + + msg = "SEND\ndestination:/app/message\n\n{\"user\":\"esp\",\"message\":\"Hello!\"}"; + sendMessage(msg); + delay(1000); + + } else { + + // do something with messages + + } + + break; + } + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + + // setup serial + + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + // USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + + + // connect to WiFi + + USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ..."); + WiFi.mode(WIFI_STA); + WiFi.begin(wlan_ssid, wlan_password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + USE_SERIAL.print("."); + } + USE_SERIAL.println(" success."); + USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP()); + + + // connect to websocket + webSocket.begin(ws_host, ws_port, stompUrl); + webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config + // webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino new file mode 100644 index 0000000..cb0c45b --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino @@ -0,0 +1,150 @@ +/* + WebSocketClientStompOverSockJs.ino + + Example for connecting and maintining a connection with a SockJS+STOMP websocket connection. + In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html). + + Created on: 18.07.2017 + Author: Martin Becker , Contact: becker@informatik.uni-wuerzburg.de +*/ + +// PRE + +#define USE_SERIAL Serial + + +// LIBRARIES + +#include +#include + +#include +#include + + +// SETTINGS + +const char* wlan_ssid = "yourssid"; +const char* wlan_password = "somepassword"; + +const char* ws_host = "the.host.net"; +const int ws_port = 80; + +// base URL for SockJS (websocket) connection +// The complete URL will look something like this(cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36): +// ws://://<3digits>//websocket +// For the default config of Spring's SockJS/STOMP support, the default base URL is "/socketentry/". +const char* ws_baseurl = "/socketentry/"; // don't forget leading and trailing "/" !!! + + +// VARIABLES + +WebSocketsClient webSocket; + + +// FUNCTIONS + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch (type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + } + break; + case WStype_TEXT: + { + // ##################### + // handle SockJs+STOMP protocol + // ##################### + + String text = (char*) payload; + + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + if (payload[0] == 'h') { + + USE_SERIAL.println("Heartbeat!"); + + } else if (payload[0] == 'o') { + + // on open connection + char *msg = "[\"CONNECT\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\"]"; + webSocket.sendTXT(msg); + + } else if (text.startsWith("a[\"CONNECTED")) { + + // subscribe to some channels + + char *msg = "[\"SUBSCRIBE\\nid:sub-0\\ndestination:/user/queue/messages\\n\\n\\u0000\"]"; + webSocket.sendTXT(msg); + delay(1000); + + // and send a message + + msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]"; + webSocket.sendTXT(msg); + delay(1000); + } + + break; + } + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + + // setup serial + + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + // USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + + + // connect to WiFi + + USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ..."); + WiFi.mode(WIFI_STA); + WiFi.begin(wlan_ssid, wlan_password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + USE_SERIAL.print("."); + } + USE_SERIAL.println(" success."); + USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP()); + + + // ##################### + // create socket url according to SockJS protocol (cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36) + // ##################### + String socketUrl = ws_baseurl; + socketUrl += random(0, 999); + socketUrl += "/"; + socketUrl += random(0, 999999); // should be a random string, but this works (see ) + socketUrl += "/websocket"; + + // connect to websocket + webSocket.begin(ws_host, ws_port, socketUrl); + webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config + // webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino new file mode 100644 index 0000000..1ac3002 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino @@ -0,0 +1,86 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial1 + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} + diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino new file mode 100644 index 0000000..5fed1a9 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino @@ -0,0 +1,132 @@ +/* + * WebSocketServerAllFunctionsDemo.ino + * + * Created on: 10.05.2018 + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define LED_RED 15 +#define LED_GREEN 12 +#define LED_BLUE 13 + +#define USE_SERIAL Serial + +ESP8266WiFiMulti WiFiMulti; + +ESP8266WebServer server(80); +WebSocketsServer webSocket = WebSocketsServer(81); + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + if(payload[0] == '#') { + // we get RGB data + + // decode rgb data + uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16); + + analogWrite(LED_RED, ((rgb >> 16) & 0xFF)); + analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF)); + analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF)); + } + + break; + } + +} + +void setup() { + //USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + pinMode(LED_RED, OUTPUT); + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_BLUE, OUTPUT); + + digitalWrite(LED_RED, 1); + digitalWrite(LED_GREEN, 1); + digitalWrite(LED_BLUE, 1); + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // start webSocket server + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + + if(MDNS.begin("esp8266")) { + USE_SERIAL.println("MDNS responder started"); + } + + // handle index + server.on("/", []() { + // send index.html + server.send(200, "text/html", "LED Control:

R:
G:
B:
"); + }); + + server.begin(); + + // Add service to MDNS + MDNS.addService("http", "tcp", 80); + MDNS.addService("ws", "tcp", 81); + + digitalWrite(LED_RED, 0); + digitalWrite(LED_GREEN, 0); + digitalWrite(LED_BLUE, 0); + +} + +unsigned long last_10sec = 0; +unsigned int counter = 0; + +void loop() { + unsigned long t = millis(); + webSocket.loop(); + server.handleClient(); + + if((t - last_10sec) > 10 * 1000) { + counter++; + bool ping = (counter % 2); + int i = webSocket.connectedClients(ping); + USE_SERIAL.printf("%d Connected websocket clients ping: %d\n", i, ping); + last_10sec = millis(); + } +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino new file mode 100644 index 0000000..84c9775 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino @@ -0,0 +1,94 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial + +String fragmentBuffer = ""; + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + break; + + // Fragmentation / continuation opcode handling + // case WStype_FRAGMENT_BIN_START: + case WStype_FRAGMENT_TEXT_START: + fragmentBuffer = (char*)payload; + USE_SERIAL.printf("[%u] get start start of Textfragment: %s\n", num, payload); + break; + case WStype_FRAGMENT: + fragmentBuffer += (char*)payload; + USE_SERIAL.printf("[%u] get Textfragment : %s\n", num, payload); + break; + case WStype_FRAGMENT_FIN: + fragmentBuffer += (char*)payload; + USE_SERIAL.printf("[%u] get end of Textfragment: %s\n", num, payload); + USE_SERIAL.printf("[%u] full frame: %s\n", num, fragmentBuffer.c_str()); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} + diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino new file mode 100644 index 0000000..8bc646c --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino @@ -0,0 +1,86 @@ +/* + * WebSocketServerHttpHeaderValidation.ino + * + * Created on: 08.06.2016 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial1 + +const unsigned long int validSessionId = 12345; //some arbitrary value to act as a valid sessionId + +/* + * Returns a bool value as an indicator to describe whether a user is allowed to initiate a websocket upgrade + * based on the value of a cookie. This function expects the rawCookieHeaderValue to look like this "sessionId=|" + */ +bool isCookieValid(String rawCookieHeaderValue) { + + if (rawCookieHeaderValue.indexOf("sessionId") != -1) { + String sessionIdStr = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("sessionId=") + 10, rawCookieHeaderValue.indexOf("|")); + unsigned long int sessionId = strtoul(sessionIdStr.c_str(), NULL, 10); + return sessionId == validSessionId; + } + return false; +} + +/* + * The WebSocketServerHttpHeaderValFunc delegate passed to webSocket.onValidateHttpHeader + */ +bool validateHttpHeader(String headerName, String headerValue) { + + //assume a true response for any headers not handled by this validator + bool valid = true; + + if(headerName.equalsIgnoreCase("Cookie")) { + //if the header passed is the Cookie header, validate it according to the rules in 'isCookieValid' function + valid = isCookieValid(headerValue); + } + + return valid; +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + //connecting clients must supply a valid session cookie at websocket upgrade handshake negotiation time + const char * headerkeys[] = { "Cookie" }; + size_t headerKeyCount = sizeof(headerkeys) / sizeof(char*); + webSocket.onValidateHttpHeader(validateHttpHeader, headerkeys, headerKeyCount); + webSocket.begin(); +} + +void loop() { + webSocket.loop(); +} + diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino new file mode 100644 index 0000000..8f32e75 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino @@ -0,0 +1,121 @@ +/* + * WebSocketServer_LEDcontrol.ino + * + * Created on: 26.11.2015 + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define LED_RED 15 +#define LED_GREEN 12 +#define LED_BLUE 13 + +#define USE_SERIAL Serial + + +ESP8266WiFiMulti WiFiMulti; + +ESP8266WebServer server(80); +WebSocketsServer webSocket = WebSocketsServer(81); + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + if(payload[0] == '#') { + // we get RGB data + + // decode rgb data + uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16); + + analogWrite(LED_RED, ((rgb >> 16) & 0xFF)); + analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF)); + analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF)); + } + + break; + } + +} + +void setup() { + //USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + pinMode(LED_RED, OUTPUT); + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_BLUE, OUTPUT); + + digitalWrite(LED_RED, 1); + digitalWrite(LED_GREEN, 1); + digitalWrite(LED_BLUE, 1); + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // start webSocket server + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + + if(MDNS.begin("esp8266")) { + USE_SERIAL.println("MDNS responder started"); + } + + // handle index + server.on("/", []() { + // send index.html + server.send(200, "text/html", "LED Control:

R:
G:
B:
"); + }); + + server.begin(); + + // Add service to MDNS + MDNS.addService("http", "tcp", 80); + MDNS.addService("ws", "tcp", 81); + + digitalWrite(LED_RED, 0); + digitalWrite(LED_GREEN, 0); + digitalWrite(LED_BLUE, 0); + +} + +void loop() { + webSocket.loop(); + server.handleClient(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp new file mode 100644 index 0000000..461228f --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp @@ -0,0 +1,46 @@ +/* To compile using make CLI, create a folder under \firmware\user\applications and copy application.cpp there. +* Then, copy src files under particleWebSocket folder. +*/ + +#include "application.h" +#include "particleWebSocket/WebSocketsClient.h" + +WebSocketsClient webSocket; + +void webSocketEvent(WStype_t type, uint8_t* payload, size_t length) +{ + switch (type) + { + case WStype_DISCONNECTED: + Serial.printlnf("[WSc] Disconnected!"); + break; + case WStype_CONNECTED: + Serial.printlnf("[WSc] Connected to URL: %s", payload); + webSocket.sendTXT("Connected\r\n"); + break; + case WStype_TEXT: + Serial.printlnf("[WSc] get text: %s", payload); + break; + case WStype_BIN: + Serial.printlnf("[WSc] get binary length: %u", length); + break; + } +} + +void setup() +{ + Serial.begin(9600); + + WiFi.setCredentials("[SSID]", "[PASSWORD]", WPA2, WLAN_CIPHER_AES_TKIP); + WiFi.connect(); + + webSocket.begin("192.168.1.153", 85, "/ClientService/?variable=Test1212"); + webSocket.onEvent(webSocketEvent); +} + +void loop() +{ + webSocket.sendTXT("Hello world!"); + delay(500); + webSocket.loop(); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/library.json b/Grbl_Esp32-master/libraries/arduinoWebSockets/library.json new file mode 100644 index 0000000..f1a5892 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/library.json @@ -0,0 +1,25 @@ +{ + "name": "WebSockets", + "description": "WebSocket Server and Client for Arduino based on RFC6455", + "keywords": "wifi, http, web, server, client, websocket", + "authors": [ + { + "name": "Markus Sattler", + "url": "https://github.com/Links2004", + "maintainer": true + } + ], + "repository": { + "type": "git", + "url": "https://github.com/Links2004/arduinoWebSockets.git" + }, + "version": "2.1.2", + "license": "LGPL-2.1", + "export": { + "exclude": [ + "tests" + ] + }, + "frameworks": "arduino", + "platforms": "atmelavr, espressif8266, espressif32" +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/library.properties b/Grbl_Esp32-master/libraries/arduinoWebSockets/library.properties new file mode 100644 index 0000000..00d0945 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/library.properties @@ -0,0 +1,9 @@ +name=WebSockets +version=2.1.2 +author=Markus Sattler +maintainer=Markus Sattler +sentence=WebSockets for Arduino (Server + Client) +paragraph=use 2.x.x for ESP and 1.3 for AVR +category=Communication +url=https://github.com/Links2004/arduinoWebSockets +architectures=* diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSockets.cpp b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSockets.cpp new file mode 100644 index 0000000..727e472 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSockets.cpp @@ -0,0 +1,655 @@ +/** + * @file WebSockets.cpp + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 "WebSockets.h" + +#ifdef ESP8266 +#include +#endif + +extern "C" { +#ifdef CORE_HAS_LIBB64 +#include +#else +#include "libb64/cencode_inc.h" +#endif +} + +#ifdef ESP8266 +#include +#elif defined(ESP32) +#include +#else + +extern "C" { +#include "libsha1/libsha1.h" +} + +#endif + + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param code uint16_t see RFC + * @param reason ptr to the disconnect reason message + * @param reasonLen length of the disconnect reason message + */ +void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code); + if(client->status == WSC_CONNECTED && code) { + if(reason) { + sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen); + } else { + uint8_t buffer[2]; + buffer[0] = ((code >> 8) & 0xFF); + buffer[1] = (code & 0xFF); + sendFrame(client, WSop_close, &buffer[0], 2); + } + } + clientDisconnect(client); +} + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * ptr to the payload + * @param length size_t length of the payload + * @param mask bool add dummy mask to the frame (needed for web browser) + * @param fin bool can be used to send data in more then one frame (set fin on the last frame) + * @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!) + * @return true if ok + */ +bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool mask, bool fin, bool headerToPayload) { + + if(client->tcp && !client->tcp->connected()) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num); + return false; + } + + if(client->status != WSC_CONNECTED) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num); + return false; + } + + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num); + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, mask, length, headerToPayload); + + if(opcode == WSop_text) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0))); + } + + uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 }; + uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 }; + + uint8_t headerSize; + uint8_t * headerPtr; + uint8_t * payloadPtr = payload; + bool useInternBuffer = false; + bool ret = true; + + // calculate header Size + if(length < 126) { + headerSize = 2; + } else if(length < 0xFFFF) { + headerSize = 4; + } else { + headerSize = 10; + } + + if(mask) { + headerSize += 4; + } + +#ifdef WEBSOCKETS_USE_BIG_MEM + // only for ESP since AVR has less HEAP + // try to send data in one TCP package (only if some free Heap is there) + if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num); + uint8_t * dataPtr = (uint8_t *) malloc(length + WEBSOCKETS_MAX_HEADER_SIZE); + if(dataPtr) { + memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length); + headerToPayload = true; + useInternBuffer = true; + payloadPtr = dataPtr; + } + } +#endif + + // set Header Pointer + if(headerToPayload) { + // calculate offset in payload + headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize)); + } else { + headerPtr = &buffer[0]; + } + + // create header + + // byte 0 + *headerPtr = 0x00; + if(fin) { + *headerPtr |= bit(7); ///< set Fin + } + *headerPtr |= opcode; ///< set opcode + headerPtr++; + + // byte 1 + *headerPtr = 0x00; + if(mask) { + *headerPtr |= bit(7); ///< set mask + } + + if(length < 126) { + *headerPtr |= length; + headerPtr++; + } else if(length < 0xFFFF) { + *headerPtr |= 126; + headerPtr++; + *headerPtr = ((length >> 8) & 0xFF); + headerPtr++; + *headerPtr = (length & 0xFF); + headerPtr++; + } else { + // Normally we never get here (to less memory) + *headerPtr |= 127; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = ((length >> 24) & 0xFF); + headerPtr++; + *headerPtr = ((length >> 16) & 0xFF); + headerPtr++; + *headerPtr = ((length >> 8) & 0xFF); + headerPtr++; + *headerPtr = (length & 0xFF); + headerPtr++; + } + + if(mask) { + if(useInternBuffer) { + // if we use a Intern Buffer we can modify the data + // by this fact its possible the do the masking + for(uint8_t x = 0; x < sizeof(maskKey); x++) { + maskKey[x] = random(0xFF); + *headerPtr = maskKey[x]; + headerPtr++; + } + + uint8_t * dataMaskPtr; + + if(headerToPayload) { + dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE); + } else { + dataMaskPtr = payloadPtr; + } + + for(size_t x = 0; x < length; x++) { + dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]); + } + + } else { + *headerPtr = maskKey[0]; + headerPtr++; + *headerPtr = maskKey[1]; + headerPtr++; + *headerPtr = maskKey[2]; + headerPtr++; + *headerPtr = maskKey[3]; + headerPtr++; + } + } + +#ifndef NODEBUG_WEBSOCKETS + unsigned long start = micros(); +#endif + + if(headerToPayload) { + // header has be added to payload + // payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings + // offset in payload is calculatetd 14 - headerSize + if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) { + ret = false; + } + } else { + // send header + if(write(client, &buffer[0], headerSize) != headerSize) { + ret = false; + } + + if(payloadPtr && length > 0) { + // send payload + if(write(client, &payloadPtr[0], length) != length) { + ret = false; + } + } + } + + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start)); + +#ifdef WEBSOCKETS_USE_BIG_MEM + if(useInternBuffer && payloadPtr) { + free(payloadPtr); + } +#endif + + return ret; +} + +/** + * callen when HTTP header is done + * @param client WSclient_t * ptr to the client struct + */ +void WebSockets::headerDone(WSclient_t * client) { + client->status = WSC_CONNECTED; + client->cWsRXsize = 0; + DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num); +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; + handleWebsocket(client); +#endif +} + +/** + * handle the WebSocket stream + * @param client WSclient_t * ptr to the client struct + */ +void WebSockets::handleWebsocket(WSclient_t * client) { + if(client->cWsRXsize == 0) { + handleWebsocketCb(client); + } +} + +/** + * wait for + * @param client + * @param size + */ +bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) { + if(!client->tcp || !client->tcp->connected()) { + return false; + } + + if(size > WEBSOCKETS_MAX_HEADER_SIZE) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size); + return false; + } + + if(client->cWsRXsize >= size) { + return true; + } + + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize); + readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok); + if(ok) { + client->cWsRXsize = size; + server->handleWebsocketCb(client); + } else { + DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num); + client->cWsRXsize = 0; + // timeout or error + server->clientDisconnect(client, 1002); + } + }, this, size, std::placeholders::_1, std::placeholders::_2)); + return false; +} + +void WebSockets::handleWebsocketCb(WSclient_t * client) { + + if(!client->tcp || !client->tcp->connected()) { + return; + } + + uint8_t * buffer = client->cWsHeader; + + WSMessageHeader_t * header = &client->cWsHeaderDecode; + uint8_t * payload = NULL; + + uint8_t headerLen = 2; + + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + + // split first 2 bytes in the data + header->fin = ((*buffer >> 7) & 0x01); + header->rsv1 = ((*buffer >> 6) & 0x01); + header->rsv2 = ((*buffer >> 5) & 0x01); + header->rsv3 = ((*buffer >> 4) & 0x01); + header->opCode = (WSopcode_t) (*buffer & 0x0F); + buffer++; + + header->mask = ((*buffer >> 7) & 0x01); + header->payloadLen = (WSopcode_t) (*buffer & 0x7F); + buffer++; + + if(header->payloadLen == 126) { + headerLen += 2; + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + header->payloadLen = buffer[0] << 8 | buffer[1]; + buffer += 2; + } else if(header->payloadLen == 127) { + headerLen += 8; + // read 64bit integer as length + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + + if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) { + // really too big! + header->payloadLen = 0xFFFFFFFF; + } else { + header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7]; + } + buffer += 8; + } + + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen); + + if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen); + clientDisconnect(client, 1009); + return; + } + + if(header->mask) { + headerLen += 4; + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + header->maskKey = buffer; + buffer += 4; + } + + if(header->payloadLen > 0) { + // if text data we need one more + payload = (uint8_t *) malloc(header->payloadLen + 1); + + if(!payload) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen); + clientDisconnect(client, 1011); + return; + } + readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload)); + } else { + handleWebsocketPayloadCb(client, true, NULL); + } +} + +void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) { + + WSMessageHeader_t * header = &client->cWsHeaderDecode; + if(ok) { + if(header->payloadLen > 0) { + payload[header->payloadLen] = 0x00; + + if(header->mask) { + //decode XOR + for(size_t i = 0; i < header->payloadLen; i++) { + payload[i] = (payload[i] ^ header->maskKey[i % 4]); + } + } + } + + switch(header->opCode) { + case WSop_text: + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload); + // no break here! + case WSop_binary: + case WSop_continuation: + messageReceived(client, header->opCode, payload, header->payloadLen, header->fin); + break; + case WSop_ping: + // send pong back + sendFrame(client, WSop_pong, payload, header->payloadLen, true); + break; + case WSop_pong: + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char*)payload : ""); + break; + case WSop_close: { + #ifndef NODEBUG_WEBSOCKETS + uint16_t reasonCode = 1000; + if(header->payloadLen >= 2) { + reasonCode = payload[0] << 8 | payload[1]; + } + #endif + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", client->num, reasonCode); + if(header->payloadLen > 2) { + DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2)); + } else { + DEBUG_WEBSOCKETS("\n"); + } + clientDisconnect(client, 1000); + } + break; + default: + clientDisconnect(client, 1002); + break; + } + + if(payload) { + free(payload); + } + + // reset input + client->cWsRXsize = 0; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + //register callback for next message + handleWebsocketWaitFor(client, 2); +#endif + + } else { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] missing data!\n", client->num); + free(payload); + clientDisconnect(client, 1002); + } +} + +/** + * generate the key for Sec-WebSocket-Accept + * @param clientKey String + * @return String Accept Key + */ +String WebSockets::acceptKey(String & clientKey) { + uint8_t sha1HashBin[20] = { 0 }; +#ifdef ESP8266 + sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]); +#elif defined(ESP32) + String data = clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + esp_sha(SHA1, (unsigned char*)data.c_str(), data.length(), &sha1HashBin[0]); +#else + clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + SHA1_CTX ctx; + SHA1Init(&ctx); + SHA1Update(&ctx, (const unsigned char*)clientKey.c_str(), clientKey.length()); + SHA1Final(&sha1HashBin[0], &ctx); +#endif + + String key = base64_encode(sha1HashBin, 20); + key.trim(); + + return key; +} + +/** + * base64_encode + * @param data uint8_t * + * @param length size_t + * @return base64 encoded String + */ +String WebSockets::base64_encode(uint8_t * data, size_t length) { + size_t size = ((length * 1.6f) + 1); + char * buffer = (char *) malloc(size); + if(buffer) { + base64_encodestate _state; + base64_init_encodestate(&_state); + int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state); + len = base64_encode_blockend((buffer + len), &_state); + + String base64 = String(buffer); + free(buffer); + return base64; + } + return String("-FAIL-"); +} + +/** + * read x byte from tcp or get timeout + * @param client WSclient_t * + * @param out uint8_t * data buffer + * @param n size_t byte count + * @return true if ok + */ +bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) { +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + if(!client->tcp || !client->tcp->connected()) { + return false; + } + + client->tcp->readBytes(out, n, std::bind([](WSclient_t * client, bool ok, WSreadWaitCb cb) { + if(cb) { + cb(client, ok); + } + }, client, std::placeholders::_1, cb)); + +#else + unsigned long t = millis(); + size_t len; + DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t); + while(n > 0) { + if(client->tcp == NULL) { + DEBUG_WEBSOCKETS("[readCb] tcp is null!\n"); + if(cb) { + cb(client, false); + } + return false; + } + + if(!client->tcp->connected()) { + DEBUG_WEBSOCKETS("[readCb] not connected!\n"); + if(cb) { + cb(client, false); + } + return false; + } + + if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) { + DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %lu\n", (millis() - t)); + if(cb) { + cb(client, false); + } + return false; + } + + if(!client->tcp->available()) { +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + continue; + } + + len = client->tcp->read((uint8_t*) out, n); + if(len) { + t = millis(); + out += len; + n -= len; + //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n); + } else { + //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n); + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + if(cb) { + cb(client, true); + } +#endif + return true; +} + +/** + * write x byte to tcp or get timeout + * @param client WSclient_t * + * @param out uint8_t * data buffer + * @param n size_t byte count + * @return bytes send + */ +size_t WebSockets::write(WSclient_t * client, uint8_t *out, size_t n) { + if(out == NULL) return 0; + if(client == NULL) return 0; + unsigned long t = millis(); + size_t len = 0; + size_t total = 0; + DEBUG_WEBSOCKETS("[write] n: %zu t: %lu\n", n, t); + while(n > 0) { + if(client->tcp == NULL) { + DEBUG_WEBSOCKETS("[write] tcp is null!\n"); + break; + } + + if(!client->tcp->connected()) { + DEBUG_WEBSOCKETS("[write] not connected!\n"); + break; + } + + if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) { + DEBUG_WEBSOCKETS("[write] write TIMEOUT! %lu\n", (millis() - t)); + break; + } + + len = client->tcp->write((const uint8_t*)out, n); + if(len) { + t = millis(); + out += len; + n -= len; + total += len; + //DEBUG_WEBSOCKETS("write %d left %d!\n", len, n); + } else { + //DEBUG_WEBSOCKETS("write %d failed left %d!\n", len, n); + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return total; +} + +size_t WebSockets::write(WSclient_t * client, const char *out) { + if(client == NULL) return 0; + if(out == NULL) return 0; + return write(client, (uint8_t*)out, strlen(out)); +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSockets.h b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSockets.h new file mode 100644 index 0000000..dee6397 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSockets.h @@ -0,0 +1,311 @@ +/** + * @file WebSockets.h + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 WEBSOCKETS_H_ +#define WEBSOCKETS_H_ + +#ifdef STM32_DEVICE +#include +#define bit(b) (1UL << (b)) // Taken directly from Arduino.h +#else +#include +#include +#endif + +#ifdef ARDUINO_ARCH_AVR +#error Version 2.x.x currently does not support Arduino with AVR since there is no support for std namespace of c++. +#error Use Version 1.x.x. (ATmega branch) +#else +#include +#endif + + +#ifndef NODEBUG_WEBSOCKETS +#ifdef DEBUG_ESP_PORT +#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) +#else +//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ ) +#endif +#endif + + +#ifndef DEBUG_WEBSOCKETS +#define DEBUG_WEBSOCKETS(...) +#define NODEBUG_WEBSOCKETS +#endif + +#if defined(ESP8266) || defined(ESP32) + +#define WEBSOCKETS_MAX_DATA_SIZE (15*1024) +#define WEBSOCKETS_USE_BIG_MEM +#define GET_FREE_HEAP ESP.getFreeHeap() +// moves all Header strings to Flash (~300 Byte) +//#define WEBSOCKETS_SAVE_RAM + +#elif defined(STM32_DEVICE) + +#define WEBSOCKETS_MAX_DATA_SIZE (15*1024) +#define WEBSOCKETS_USE_BIG_MEM +#define GET_FREE_HEAP System.freeMemory() + +#else + +//atmega328p has only 2KB ram! +#define WEBSOCKETS_MAX_DATA_SIZE (1024) +// moves all Header strings to Flash +#define WEBSOCKETS_SAVE_RAM + +#endif + + +#define WEBSOCKETS_TCP_TIMEOUT (2000) + +#define NETWORK_ESP8266_ASYNC (0) +#define NETWORK_ESP8266 (1) +#define NETWORK_W5100 (2) +#define NETWORK_ENC28J60 (3) +#define NETWORK_ESP32 (4) + +// max size of the WS Message Header +#define WEBSOCKETS_MAX_HEADER_SIZE (14) + +#if !defined(WEBSOCKETS_NETWORK_TYPE) +// select Network type based +#if defined(ESP8266) || defined(ESP31B) +#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266 +//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC +//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 + +#elif defined(ESP32) +#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32 + +#else +#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 + +#endif +#endif + +// Includes and defined based on Network Type +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + +// Note: +// No SSL/WSS support for client in Async mode +// TLS lib need a sync interface! + + +#if defined(ESP8266) +#include +#elif defined(ESP32) +#include +#include +#elif defined(ESP31B) +#include +#else +#error "network type ESP8266 ASYNC only possible on the ESP mcu!" +#endif + +#include +#include +#define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer +#define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + +#if !defined(ESP8266) && !defined(ESP31B) +#error "network type ESP8266 only possible on the ESP mcu!" +#endif + +#ifdef ESP8266 +#include +#else +#include +#endif +#define WEBSOCKETS_NETWORK_CLASS WiFiClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100) + +#ifdef STM32_DEVICE +#define WEBSOCKETS_NETWORK_CLASS TCPClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer +#else +#include +#include +#define WEBSOCKETS_NETWORK_CLASS EthernetClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer +#endif + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60) + +#include +#define WEBSOCKETS_NETWORK_CLASS UIPClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + +#include +#include +#define WEBSOCKETS_NETWORK_CLASS WiFiClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer + +#else +#error "no network type selected!" +#endif + +// moves all Header strings to Flash (~300 Byte) +#ifdef WEBSOCKETS_SAVE_RAM +#define WEBSOCKETS_STRING(var) F(var) +#else +#define WEBSOCKETS_STRING(var) var +#endif + +typedef enum { + WSC_NOT_CONNECTED, + WSC_HEADER, + WSC_CONNECTED +} WSclientsStatus_t; + +typedef enum { + WStype_ERROR, + WStype_DISCONNECTED, + WStype_CONNECTED, + WStype_TEXT, + WStype_BIN, + WStype_FRAGMENT_TEXT_START, + WStype_FRAGMENT_BIN_START, + WStype_FRAGMENT, + WStype_FRAGMENT_FIN, +} WStype_t; + +typedef enum { + WSop_continuation = 0x00, ///< %x0 denotes a continuation frame + WSop_text = 0x01, ///< %x1 denotes a text frame + WSop_binary = 0x02, ///< %x2 denotes a binary frame + ///< %x3-7 are reserved for further non-control frames + WSop_close = 0x08, ///< %x8 denotes a connection close + WSop_ping = 0x09, ///< %x9 denotes a ping + WSop_pong = 0x0A ///< %xA denotes a pong + ///< %xB-F are reserved for further control frames +} WSopcode_t; + +typedef struct { + + bool fin; + bool rsv1; + bool rsv2; + bool rsv3; + + WSopcode_t opCode; + bool mask; + + size_t payloadLen; + + uint8_t * maskKey; +} WSMessageHeader_t; + +typedef struct { + uint8_t num; ///< connection number + + WSclientsStatus_t status; + + WEBSOCKETS_NETWORK_CLASS * tcp; + + bool isSocketIO; ///< client for socket.io server + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + bool isSSL; ///< run in ssl mode + WiFiClientSecure * ssl; +#endif + + String cUrl; ///< http url + uint16_t cCode; ///< http code + + bool cIsUpgrade; ///< Connection == Upgrade + bool cIsWebsocket; ///< Upgrade == websocket + + String cSessionId; ///< client Set-Cookie (session id) + String cKey; ///< client Sec-WebSocket-Key + String cAccept; ///< client Sec-WebSocket-Accept + String cProtocol; ///< client Sec-WebSocket-Protocol + String cExtensions; ///< client Sec-WebSocket-Extensions + uint16_t cVersion; ///< client Sec-WebSocket-Version + + uint8_t cWsRXsize; ///< State of the RX + uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer + WSMessageHeader_t cWsHeaderDecode; + + String base64Authorization; ///< Base64 encoded Auth request + String plainAuthorization; ///< Base64 encoded Auth request + + String extraHeaders; + + bool cHttpHeadersValid; ///< non-websocket http header validity indicator + size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + String cHttpLine; ///< HTTP header lines +#endif + +} WSclient_t; + + + +class WebSockets { + protected: +#ifdef __AVR__ + typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok); +#else + typedef std::function WSreadWaitCb; +#endif + + virtual void clientDisconnect(WSclient_t * client) = 0; + virtual bool clientIsConnected(WSclient_t * client) = 0; + + virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0; + + void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0); + bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool mask = false, bool fin = true, bool headerToPayload = false); + + void headerDone(WSclient_t * client); + + void handleWebsocket(WSclient_t * client); + + bool handleWebsocketWaitFor(WSclient_t * client, size_t size); + void handleWebsocketCb(WSclient_t * client); + void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload); + + String acceptKey(String & clientKey); + String base64_encode(uint8_t * data, size_t length); + + bool readCb(WSclient_t * client, uint8_t *out, size_t n, WSreadWaitCb cb); + virtual size_t write(WSclient_t * client, uint8_t *out, size_t n); + size_t write(WSclient_t * client, const char *out); + + +}; + +#ifndef UNUSED +#define UNUSED(var) (void)(var) +#endif +#endif /* WEBSOCKETS_H_ */ diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsClient.cpp b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsClient.cpp new file mode 100644 index 0000000..f98822a --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsClient.cpp @@ -0,0 +1,762 @@ +/** + * @file WebSocketsClient.cpp + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 "WebSockets.h" +#include "WebSocketsClient.h" + +WebSocketsClient::WebSocketsClient() { + _cbEvent = NULL; + _client.num = 0; + _client.extraHeaders = WEBSOCKETS_STRING("Origin: file://"); +} + +WebSocketsClient::~WebSocketsClient() { + disconnect(); +} + +/** + * calles to init the Websockets server + */ +void WebSocketsClient::begin(const char *host, uint16_t port, const char * url, const char * protocol) { + _host = host; + _port = port; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + _fingerprint = ""; +#endif + + _client.num = 0; + _client.status = WSC_NOT_CONNECTED; + _client.tcp = NULL; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + _client.isSSL = false; + _client.ssl = NULL; +#endif + _client.cUrl = url; + _client.cCode = 0; + _client.cIsUpgrade = false; + _client.cIsWebsocket = true; + _client.cKey = ""; + _client.cAccept = ""; + _client.cProtocol = protocol; + _client.cExtensions = ""; + _client.cVersion = 0; + _client.base64Authorization = ""; + _client.plainAuthorization = ""; + _client.isSocketIO = false; + +#ifdef ESP8266 + randomSeed(RANDOM_REG32); +#else + // todo find better seed + randomSeed(millis()); +#endif +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + asyncConnect(); +#endif + + _lastConnectionFail = 0; + _reconnectInterval = 500; +} + +void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) { + begin(host.c_str(), port, url.c_str(), protocol.c_str()); +} + +void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, const char * protocol) { + return begin(host.toString().c_str(), port, url, protocol); +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) +void WebSocketsClient::beginSSL(const char *host, uint16_t port, const char * url, const char * fingerprint, const char * protocol) { + begin(host, port, url, protocol); + _client.isSSL = true; + _fingerprint = fingerprint; +} + +void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) { + beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str(), protocol.c_str()); +} +#endif + +void WebSocketsClient::beginSocketIO(const char *host, uint16_t port, const char * url, const char * protocol) { + begin(host, port, url, protocol); + _client.isSocketIO = true; +} + +void WebSocketsClient::beginSocketIO(String host, uint16_t port, String url, String protocol) { + beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str()); +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) +void WebSocketsClient::beginSocketIOSSL(const char *host, uint16_t port, const char * url, const char * protocol) { + begin(host, port, url, protocol); + _client.isSocketIO = true; + _client.isSSL = true; + _fingerprint = ""; +} + +void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) { + beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str()); +} +#endif + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * called in arduino loop + */ +void WebSocketsClient::loop(void) { + if(!clientIsConnected(&_client)) { + // do not flood the server + if((millis() - _lastConnectionFail) < _reconnectInterval) { + return; + } + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + if(_client.isSSL) { + DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n"); + if(_client.ssl) { + delete _client.ssl; + _client.ssl = NULL; + _client.tcp = NULL; + } + _client.ssl = new WiFiClientSecure(); + _client.tcp = _client.ssl; + } else { + DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n"); + if(_client.tcp) { + delete _client.tcp; + _client.tcp = NULL; + } + _client.tcp = new WiFiClient(); + } +#else + _client.tcp = new WEBSOCKETS_NETWORK_CLASS(); +#endif + + if(!_client.tcp) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); + return; + } + + if(_client.tcp->connect(_host.c_str(), _port)) { + connectedCb(); + _lastConnectionFail = 0; + } else { + connectFailedCb(); + _lastConnectionFail = millis(); + + } + } else { + handleClientData(); + } +} +#endif + +/** + * set callback function + * @param cbEvent WebSocketServerEvent + */ +void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) { + _cbEvent = cbEvent; +} + +/** + * send text data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) { + if(length == 0) { + length = strlen((const char *) payload); + } + if(clientIsConnected(&_client)) { + return sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload); + } + return false; +} + +bool WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) { + return sendTXT((uint8_t *) payload, length); +} + +bool WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) { + return sendTXT((uint8_t *) payload, length, headerToPayload); +} + +bool WebSocketsClient::sendTXT(const char * payload, size_t length) { + return sendTXT((uint8_t *) payload, length); +} + +bool WebSocketsClient::sendTXT(String & payload) { + return sendTXT((uint8_t *) payload.c_str(), payload.length()); +} + +/** + * send binary data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) { + if(clientIsConnected(&_client)) { + return sendFrame(&_client, WSop_binary, payload, length, true, true, headerToPayload); + } + return false; +} + +bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) { + return sendBIN((uint8_t *) payload, length); +} + +/** + * sends a WS ping to Server + * @param payload uint8_t * + * @param length size_t + * @return true if ping is send out + */ +bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) { + if(clientIsConnected(&_client)) { + return sendFrame(&_client, WSop_ping, payload, length, true); + } + return false; +} + +bool WebSocketsClient::sendPing(String & payload) { + return sendPing((uint8_t *) payload.c_str(), payload.length()); +} + +/** + * disconnect one client + * @param num uint8_t client id + */ +void WebSocketsClient::disconnect(void) { + if(clientIsConnected(&_client)) { + WebSockets::clientDisconnect(&_client, 1000); + } +} + +/** + * set the Authorizatio for the http request + * @param user const char * + * @param password const char * + */ +void WebSocketsClient::setAuthorization(const char * user, const char * password) { + if(user && password) { + String auth = user; + auth += ":"; + auth += password; + _client.base64Authorization = base64_encode((uint8_t *) auth.c_str(), auth.length()); + } +} + +/** + * set the Authorizatio for the http request + * @param auth const char * base64 + */ +void WebSocketsClient::setAuthorization(const char * auth) { + if(auth) { + //_client.base64Authorization = auth; + _client.plainAuthorization = auth; + } +} + +/** + * set extra headers for the http request; + * separate headers by "\r\n" + * @param extraHeaders const char * extraHeaders + */ +void WebSocketsClient::setExtraHeaders(const char * extraHeaders) { + _client.extraHeaders = extraHeaders; +} + +/** + * set the reconnect Interval + * how long to wait after a connection initiate failed + * @param time in ms + */ +void WebSocketsClient::setReconnectInterval(unsigned long time) { + _reconnectInterval = time; +} + +//################################################################################# +//################################################################################# +//################################################################################# + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) { + WStype_t type = WStype_ERROR; + + UNUSED(client); + + switch(opcode) { + case WSop_text: + type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START; + break; + case WSop_binary: + type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START; + break; + case WSop_continuation: + type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT; + break; + case WSop_close: + case WSop_ping: + case WSop_pong: + default: + break; + } + + runCbEvent(type, payload, length); + +} + +/** + * Disconnect an client + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsClient::clientDisconnect(WSclient_t * client) { + + bool event = false; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + if(client->isSSL && client->ssl) { + if(client->ssl->connected()) { + client->ssl->flush(); + client->ssl->stop(); + } + event = true; + delete client->ssl; + client->ssl = NULL; + client->tcp = NULL; + } +#endif + + if(client->tcp) { + if(client->tcp->connected()) { +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + client->tcp->flush(); +#endif + client->tcp->stop(); + } + event = true; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->status = WSC_NOT_CONNECTED; +#else + delete client->tcp; +#endif + client->tcp = NULL; + } + + client->cCode = 0; + client->cKey = ""; + client->cAccept = ""; + client->cVersion = 0; + client->cIsUpgrade = false; + client->cIsWebsocket = false; + client->cSessionId = ""; + + client->status = WSC_NOT_CONNECTED; + + DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n"); + if(event) { + runCbEvent(WStype_DISCONNECTED, NULL, 0); + } +} + +/** + * get client state + * @param client WSclient_t * ptr to the client struct + * @return true = conneted + */ +bool WebSocketsClient::clientIsConnected(WSclient_t * client) { + + if(!client->tcp) { + return false; + } + + if(client->tcp->connected()) { + if(client->status != WSC_NOT_CONNECTED) { + return true; + } + } else { + // client lost + if(client->status != WSC_NOT_CONNECTED) { + DEBUG_WEBSOCKETS("[WS-Client] connection lost.\n"); + // do cleanup + clientDisconnect(client); + } + } + + if(client->tcp) { + // do cleanup + clientDisconnect(client); + } + + return false; +} +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * Handel incomming data from Client + */ +void WebSocketsClient::handleClientData(void) { + int len = _client.tcp->available(); + if(len > 0) { + switch(_client.status) { + case WSC_HEADER: { + String headerLine = _client.tcp->readStringUntil('\n'); + handleHeader(&_client, &headerLine); + } + break; + case WSC_CONNECTED: + WebSockets::handleWebsocket(&_client); + break; + default: + WebSockets::clientDisconnect(&_client, 1002); + break; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + delay(0); +#endif +} +#endif + +/** + * send the WebSocket header to Server + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsClient::sendHeader(WSclient_t * client) { + + static const char * NEW_LINE = "\r\n"; + + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n"); + + uint8_t randomKey[16] = { 0 }; + + for(uint8_t i = 0; i < sizeof(randomKey); i++) { + randomKey[i] = random(0xFF); + } + + client->cKey = base64_encode(&randomKey[0], 16); + +#ifndef NODEBUG_WEBSOCKETS + unsigned long start = micros(); +#endif + + String handshake; + bool ws_header = true; + String url = client->cUrl; + + if(client->isSocketIO) { + if(client->cSessionId.length() == 0) { + url += WEBSOCKETS_STRING("&transport=polling"); + ws_header = false; + } else { + url += WEBSOCKETS_STRING("&transport=websocket&sid="); + url += client->cSessionId; + } + } + + handshake = WEBSOCKETS_STRING("GET "); + handshake += url + WEBSOCKETS_STRING(" HTTP/1.1\r\n" + "Host: "); + handshake += _host + ":" + _port + NEW_LINE; + + if(ws_header) { + handshake += WEBSOCKETS_STRING("Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: "); + handshake += client->cKey + NEW_LINE; + + if(client->cProtocol.length() > 0) { + handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: "); + handshake += client->cProtocol + NEW_LINE; + } + + if(client->cExtensions.length() > 0) { + handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: "); + handshake += client->cExtensions + NEW_LINE; + } + } else { + handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n"); + } + + // add extra headers; by default this includes "Origin: file://" + if(client->extraHeaders) { + handshake += client->extraHeaders + NEW_LINE; + } + + handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n"); + + if(client->base64Authorization.length() > 0) { + handshake += WEBSOCKETS_STRING("Authorization: Basic "); + handshake += client->base64Authorization + NEW_LINE; + } + + if(client->plainAuthorization.length() > 0) { + handshake += WEBSOCKETS_STRING("Authorization: "); + handshake += client->plainAuthorization + NEW_LINE; + } + + handshake += NEW_LINE; + + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t* )handshake.c_str()); + write(client, (uint8_t*) handshake.c_str(), handshake.length()); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); +#endif + + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%luus).\n", (micros() - start)); + +} + +/** + * handle the WebSocket header reading + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { + + headerLine->trim(); // remove \r + + if(headerLine->length() > 0) { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str()); + + if(headerLine->startsWith(WEBSOCKETS_STRING("HTTP/1."))) { + // "HTTP/1.1 101 Switching Protocols" + client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt(); + } else if(headerLine->indexOf(':')) { + String headerName = headerLine->substring(0, headerLine->indexOf(':')); + String headerValue = headerLine->substring(headerLine->indexOf(':') + 1); + + // remove space in the beginning (RFC2616) + if(headerValue[0] == ' ') { + headerValue.remove(0, 1); + } + + if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) { + if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) { + client->cIsUpgrade = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) { + if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) { + client->cIsWebsocket = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) { + client->cAccept = headerValue; + client->cAccept.trim(); // see rfc6455 + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) { + client->cProtocol = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) { + client->cExtensions = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) { + client->cVersion = headerValue.toInt(); + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) { + if(headerValue.indexOf(WEBSOCKETS_STRING("HttpOnly")) > -1) { + client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";")); + } else { + client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1); + } + } + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); + } + + (*headerLine) = ""; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); +#endif + + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n"); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n"); + + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cURL: %s\n", client->cUrl.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cKey: %s\n", client->cKey.c_str()); + + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Server header:\n"); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cCode: %d\n", client->cCode); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsUpgrade: %d\n", client->cIsUpgrade); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsWebsocket: %d\n", client->cIsWebsocket); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cAccept: %s\n", client->cAccept.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str()); + + bool ok = (client->cIsUpgrade && client->cIsWebsocket); + + if(ok) { + switch(client->cCode) { + case 101: ///< Switching Protocols + + break; + case 200: + if(client->isSocketIO) { + break; + } + case 403: ///< Forbidden + // todo handle login + default: ///< Server dont unterstand requrst + ok = false; + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode); + clientDisconnect(client); + _lastConnectionFail = millis(); + break; + } + } + + if(ok) { + + if(client->cAccept.length() == 0) { + ok = false; + } else { + // generate Sec-WebSocket-Accept key for check + String sKey = acceptKey(client->cKey); + if(sKey != client->cAccept) { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Sec-WebSocket-Accept is wrong\n"); + ok = false; + } + } + } + + if(ok) { + + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n"); + headerDone(client); + + runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); + + } else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) { + sendHeader(client); + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n"); + _lastConnectionFail = millis(); + if(clientIsConnected(client)) { + write(client, "This is a webSocket client!"); + } + clientDisconnect(client); + } + } +} + +void WebSocketsClient::connectedCb() { + + DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _client.tcp->onDisconnect(std::bind([](WebSocketsClient * c, AsyncTCPbuffer * obj, WSclient_t * client) -> bool { + DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num); + client->status = WSC_NOT_CONNECTED; + client->tcp = NULL; + + // reconnect + c->asyncConnect(); + + return true; + }, this, std::placeholders::_1, &_client)); +#endif + + _client.status = WSC_HEADER; + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + // set Timeout for readBytesUntil and readStringUntil + _client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); +#endif + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + _client.tcp->setNoDelay(true); + + if(_client.isSSL && _fingerprint.length()) { + if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) { + DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n"); + WebSockets::clientDisconnect(&_client, 1000); + return; + } + } +#endif + + // send Header to Server + sendHeader(&_client); + +} + +void WebSocketsClient::connectFailedCb() { + DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n", _host.c_str(), _port); +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + +void WebSocketsClient::asyncConnect() { + + DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n"); + + AsyncClient * tcpclient = new AsyncClient(); + + if(!tcpclient) { + DEBUG_WEBSOCKETS("[WS-Client] creating AsyncClient class failed!\n"); + return; + } + + tcpclient->onDisconnect([](void *obj, AsyncClient* c) { + c->free(); + delete c; + }); + + tcpclient->onConnect(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) { + ws->_client.tcp = new AsyncTCPbuffer(tcp); + if(!ws->_client.tcp) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n"); + ws->connectFailedCb(); + return; + } + ws->connectedCb(); + }, this, std::placeholders::_2)); + + tcpclient->onError(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) { + ws->connectFailedCb(); + + // reconnect + ws->asyncConnect(); + }, this, std::placeholders::_2)); + + if(!tcpclient->connect(_host.c_str(), _port)) { + connectFailedCb(); + delete tcpclient; + } + +} + +#endif diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsClient.h b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsClient.h new file mode 100644 index 0000000..61b8ea2 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsClient.h @@ -0,0 +1,136 @@ +/** + * @file WebSocketsClient.h + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 WEBSOCKETSCLIENT_H_ +#define WEBSOCKETSCLIENT_H_ + +#include "WebSockets.h" + +class WebSocketsClient: private WebSockets { + public: +#ifdef __AVR__ + typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length); +#else + typedef std::function WebSocketClientEvent; +#endif + + + WebSocketsClient(void); + virtual ~WebSocketsClient(void); + + void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); + void begin(String host, uint16_t port, String url = "/", String protocol = "arduino"); + void begin(IPAddress host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + void beginSSL(const char *host, uint16_t port, const char * url = "/", const char * = "", const char * protocol = "arduino"); + void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino"); +#endif + + void beginSocketIO(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); + void beginSocketIO(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + void beginSocketIOSSL(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); + void beginSocketIOSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); +#endif + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void loop(void); +#else + // Async interface not need a loop call + void loop(void) __attribute__ ((deprecated)) {} +#endif + + void onEvent(WebSocketClientEvent cbEvent); + + bool sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(const uint8_t * payload, size_t length = 0); + bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(const char * payload, size_t length = 0); + bool sendTXT(String & payload); + + bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false); + bool sendBIN(const uint8_t * payload, size_t length); + + bool sendPing(uint8_t * payload = NULL, size_t length = 0); + bool sendPing(String & payload); + + void disconnect(void); + + void setAuthorization(const char * user, const char * password); + void setAuthorization(const char * auth); + + void setExtraHeaders(const char * extraHeaders = NULL); + + void setReconnectInterval(unsigned long time); + + protected: + String _host; + uint16_t _port; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + String _fingerprint; +#endif + WSclient_t _client; + + WebSocketClientEvent _cbEvent; + + unsigned long _lastConnectionFail; + unsigned long _reconnectInterval; + + void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); + + void clientDisconnect(WSclient_t * client); + bool clientIsConnected(WSclient_t * client); + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void handleClientData(void); +#endif + + void sendHeader(WSclient_t * client); + void handleHeader(WSclient_t * client, String * headerLine); + + void connectedCb(); + void connectFailedCb(); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + void asyncConnect(); +#endif + + /** + * called for sending a Event to the app + * @param type WStype_t + * @param payload uint8_t * + * @param length size_t + */ + virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) { + if(_cbEvent) { + _cbEvent(type, payload, length); + } + } + +}; + +#endif /* WEBSOCKETSCLIENT_H_ */ diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsServer.cpp b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsServer.cpp new file mode 100644 index 0000000..b6f950f --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsServer.cpp @@ -0,0 +1,873 @@ +/** + * @file WebSocketsServer.cpp + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 "WebSockets.h" +#include "WebSocketsServer.h" + +WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol) { + _port = port; + _origin = origin; + _protocol = protocol; + _runnning = false; + + _server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _server->onClient([](void *s, AsyncClient* c){ + ((WebSocketsServer*)s)->newClient(new AsyncTCPbuffer(c)); + }, this); +#endif + + _cbEvent = NULL; + + _httpHeaderValidationFunc = NULL; + _mandatoryHttpHeaders = NULL; + _mandatoryHttpHeaderCount = 0; + + memset(&_clients[0], 0x00, (sizeof(WSclient_t) * WEBSOCKETS_SERVER_CLIENT_MAX)); +} + + +WebSocketsServer::~WebSocketsServer() { + // disconnect all clients + close(); + + if (_mandatoryHttpHeaders) + delete[] _mandatoryHttpHeaders; + + _mandatoryHttpHeaderCount = 0; +} + +/** + * called to initialize the Websocket server + */ +void WebSocketsServer::begin(void) { + WSclient_t * client; + + // init client storage + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + + client->num = i; + client->status = WSC_NOT_CONNECTED; + client->tcp = NULL; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + client->isSSL = false; + client->ssl = NULL; +#endif + client->cUrl = ""; + client->cCode = 0; + client->cKey = ""; + client->cProtocol = ""; + client->cVersion = 0; + client->cIsUpgrade = false; + client->cIsWebsocket = false; + + client->base64Authorization = ""; + + client->cWsRXsize = 0; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; +#endif + } + +#ifdef ESP8266 + randomSeed(RANDOM_REG32); +#elif defined(ESP32) + #define DR_REG_RNG_BASE 0x3ff75144 + randomSeed(READ_PERI_REG(DR_REG_RNG_BASE)); +#else + // TODO find better seed + randomSeed(millis()); +#endif + + _runnning = true; + _server->begin(); + + DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n"); +} + +void WebSocketsServer::close(void) { + _runnning = false; + disconnect(); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + _server->close(); +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _server->end(); +#else + // TODO how to close server? +#endif + +} + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * called in arduino loop + */ +void WebSocketsServer::loop(void) { + if(_runnning) { + handleNewClients(); + handleClientData(); + } +} +#endif + +/** + * set callback function + * @param cbEvent WebSocketServerEvent + */ +void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) { + _cbEvent = cbEvent; +} + +/* + * Sets the custom http header validator function + * @param httpHeaderValidationFunc WebSocketServerHttpHeaderValFunc ///< pointer to the custom http header validation function + * @param mandatoryHttpHeaders[] const char* ///< the array of named http headers considered to be mandatory / must be present in order for websocket upgrade to succeed + * @param mandatoryHttpHeaderCount size_t ///< the number of items in the mandatoryHttpHeaders array + */ +void WebSocketsServer::onValidateHttpHeader( + WebSocketServerHttpHeaderValFunc validationFunc, + const char* mandatoryHttpHeaders[], + size_t mandatoryHttpHeaderCount) +{ + _httpHeaderValidationFunc = validationFunc; + + if (_mandatoryHttpHeaders) + delete[] _mandatoryHttpHeaders; + + _mandatoryHttpHeaderCount = mandatoryHttpHeaderCount; + _mandatoryHttpHeaders = new String[_mandatoryHttpHeaderCount]; + + for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) { + _mandatoryHttpHeaders[i] = mandatoryHttpHeaders[i]; + } +} + +/* + * send text data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return false; + } + if(length == 0) { + length = strlen((const char *) payload); + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return sendFrame(client, WSop_text, payload, length, false, true, headerToPayload); + } + return false; +} + +bool WebSocketsServer::sendTXT(uint8_t num, const uint8_t * payload, size_t length) { + return sendTXT(num, (uint8_t *) payload, length); +} + +bool WebSocketsServer::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) { + return sendTXT(num, (uint8_t *) payload, length, headerToPayload); +} + +bool WebSocketsServer::sendTXT(uint8_t num, const char * payload, size_t length) { + return sendTXT(num, (uint8_t *) payload, length); +} + +bool WebSocketsServer::sendTXT(uint8_t num, String & payload) { + return sendTXT(num, (uint8_t *) payload.c_str(), payload.length()); +} + +/** + * send text data to client all + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) { + WSclient_t * client; + bool ret = true; + if(length == 0) { + length = strlen((const char *) payload); + } + + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + if(!sendFrame(client, WSop_text, payload, length, false, true, headerToPayload)) { + ret = false; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return ret; +} + +bool WebSocketsServer::broadcastTXT(const uint8_t * payload, size_t length) { + return broadcastTXT((uint8_t *) payload, length); +} + +bool WebSocketsServer::broadcastTXT(char * payload, size_t length, bool headerToPayload) { + return broadcastTXT((uint8_t *) payload, length, headerToPayload); +} + +bool WebSocketsServer::broadcastTXT(const char * payload, size_t length) { + return broadcastTXT((uint8_t *) payload, length); +} + +bool WebSocketsServer::broadcastTXT(String & payload) { + return broadcastTXT((uint8_t *) payload.c_str(), payload.length()); +} + +/** + * send binary data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return false; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload); + } + return false; +} + +bool WebSocketsServer::sendBIN(uint8_t num, const uint8_t * payload, size_t length) { + return sendBIN(num, (uint8_t *) payload, length); +} + +/** + * send binary data to client all + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload) { + WSclient_t * client; + bool ret = true; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + if(!sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload)) { + ret = false; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return ret; +} + +bool WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) { + return broadcastBIN((uint8_t *) payload, length); +} + + +/** + * sends a WS ping to Client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @return true if ping is send out + */ +bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return false; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return sendFrame(client, WSop_ping, payload, length); + } + return false; +} + +bool WebSocketsServer::sendPing(uint8_t num, String & payload) { + return sendPing(num, (uint8_t *) payload.c_str(), payload.length()); +} + +/** + * sends a WS ping to all Client + * @param payload uint8_t * + * @param length size_t + * @return true if ping is send out + */ +bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) { + WSclient_t * client; + bool ret = true; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + if(!sendFrame(client, WSop_ping, payload, length)) { + ret = false; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return ret; +} + +bool WebSocketsServer::broadcastPing(String & payload) { + return broadcastPing((uint8_t *) payload.c_str(), payload.length()); +} + + +/** + * disconnect all clients + */ +void WebSocketsServer::disconnect(void) { + WSclient_t * client; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + WebSockets::clientDisconnect(client, 1000); + } + } +} + +/** + * disconnect one client + * @param num uint8_t client id + */ +void WebSocketsServer::disconnect(uint8_t num) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + WebSockets::clientDisconnect(client, 1000); + } +} + + +/* + * set the Authorization for the http request + * @param user const char * + * @param password const char * + */ +void WebSocketsServer::setAuthorization(const char * user, const char * password) { + if(user && password) { + String auth = user; + auth += ":"; + auth += password; + _base64Authorization = base64_encode((uint8_t *)auth.c_str(), auth.length()); + } +} + +/** + * set the Authorizatio for the http request + * @param auth const char * base64 + */ +void WebSocketsServer::setAuthorization(const char * auth) { + if(auth) { + _base64Authorization = auth; + } +} + +/** + * count the connected clients (optional ping them) + * @param ping bool ping the connected clients + */ +int WebSocketsServer::connectedClients(bool ping) { + WSclient_t * client; + int count = 0; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(client->status == WSC_CONNECTED) { + if(ping != true || sendPing(i)) { + count++; + } + } + } + return count; +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) +/** + * get an IP for a client + * @param num uint8_t client id + * @return IPAddress + */ +IPAddress WebSocketsServer::remoteIP(uint8_t num) { + if(num < WEBSOCKETS_SERVER_CLIENT_MAX) { + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return client->tcp->remoteIP(); + } + } + + return IPAddress(); +} +#endif + +//################################################################################# +//################################################################################# +//################################################################################# + +/** + * handle new client connection + * @param client + */ +bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { + WSclient_t * client; + // search free list entry for client + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + + // state is not connected or tcp connection is lost + if(!clientIsConnected(client)) { + + client->tcp = TCPclient; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + client->isSSL = false; + client->tcp->setNoDelay(true); +#endif +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + // set Timeout for readBytesUntil and readStringUntil + client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); +#endif + client->status = WSC_HEADER; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + IPAddress ip = client->tcp->remoteIP(); + DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]); +#else + DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num); +#endif + + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->onDisconnect(std::bind([](WebSocketsServer * server, AsyncTCPbuffer * obj, WSclient_t * client) -> bool { + DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num); + + AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp; + if(*sl == obj) { + client->status = WSC_NOT_CONNECTED; + *sl = NULL; + } + return true; + }, this, std::placeholders::_1, client)); + + + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); +#endif + + return true; + break; + } + } + return false; +} + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) { + WStype_t type = WStype_ERROR; + + switch(opcode) { + case WSop_text: + type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START; + break; + case WSop_binary: + type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START; + break; + case WSop_continuation: + type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT; + break; + case WSop_close: + case WSop_ping: + case WSop_pong: + default: + break; + } + + runCbEvent(client->num, type, payload, length); + +} + +/** + * Disconnect an client + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsServer::clientDisconnect(WSclient_t * client) { + + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + if(client->isSSL && client->ssl) { + if(client->ssl->connected()) { + client->ssl->flush(); + client->ssl->stop(); + } + delete client->ssl; + client->ssl = NULL; + client->tcp = NULL; + } +#endif + + if(client->tcp) { + if(client->tcp->connected()) { +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + client->tcp->flush(); +#endif + client->tcp->stop(); + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->status = WSC_NOT_CONNECTED; +#else + delete client->tcp; +#endif + client->tcp = NULL; + } + + client->cUrl = ""; + client->cKey = ""; + client->cProtocol = ""; + client->cVersion = 0; + client->cIsUpgrade = false; + client->cIsWebsocket = false; + + client->cWsRXsize = 0; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; +#endif + + client->status = WSC_NOT_CONNECTED; + + DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num); + + runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0); + +} + +/** + * get client state + * @param client WSclient_t * ptr to the client struct + * @return true = connected + */ +bool WebSocketsServer::clientIsConnected(WSclient_t * client) { + + if(!client->tcp) { + return false; + } + + if(client->tcp->connected()) { + if(client->status != WSC_NOT_CONNECTED) { + return true; + } + } else { + // client lost + if(client->status != WSC_NOT_CONNECTED) { + DEBUG_WEBSOCKETS("[WS-Server][%d] client connection lost.\n", client->num); + // do cleanup + clientDisconnect(client); + } + } + + if(client->tcp) { + // do cleanup + DEBUG_WEBSOCKETS("[WS-Server][%d] client list cleanup.\n", client->num); + clientDisconnect(client); + } + + return false; +} +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * Handle incoming Connection Request + */ +void WebSocketsServer::handleNewClients(void) { + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + while(_server->hasClient()) { +#endif + bool ok = false; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + // store new connection + WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); +#else + WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); +#endif + + if(!tcpClient) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); + return; + } + + ok = newClient(tcpClient); + + if(!ok) { + // no free space to handle client +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + IPAddress ip = tcpClient->remoteIP(); + DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); +#else + DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n"); +#endif + tcpClient->stop(); + } + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + delay(0); + } +#endif + +} + + +/** + * Handel incomming data from Client + */ +void WebSocketsServer::handleClientData(void) { + + WSclient_t * client; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + int len = client->tcp->available(); + if(len > 0) { + //DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len); + switch(client->status) { + case WSC_HEADER: + { + String headerLine = client->tcp->readStringUntil('\n'); + handleHeader(client, &headerLine); + } + break; + case WSC_CONNECTED: + WebSockets::handleWebsocket(client); + break; + default: + WebSockets::clientDisconnect(client, 1002); + break; + } + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } +} +#endif + +/* + * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection + * @param headerName String ///< the name of the header being checked + */ +bool WebSocketsServer::hasMandatoryHeader(String headerName) { + for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) { + if (_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName)) + return true; + } + return false; +} + + +/** + * handles http header reading for WebSocket upgrade + * @param client WSclient_t * ///< pointer to the client struct + * @param headerLine String ///< the header being read / processed + */ +void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { + + static const char * NEW_LINE = "\r\n"; + + headerLine->trim(); // remove \r + + if(headerLine->length() > 0) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str()); + + // websocket requests always start with GET see rfc6455 + if(headerLine->startsWith("GET ")) { + + // cut URL out + client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4)); + + //reset non-websocket http header validation state for this client + client->cHttpHeadersValid = true; + client->cMandatoryHeadersCount = 0; + + } else if(headerLine->indexOf(':')) { + String headerName = headerLine->substring(0, headerLine->indexOf(':')); + String headerValue = headerLine->substring(headerLine->indexOf(':') + 1); + + // remove space in the beginning (RFC2616) + if(headerValue[0] == ' ') { + headerValue.remove(0, 1); + } + + if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) { + headerValue.toLowerCase(); + if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) { + client->cIsUpgrade = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) { + if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) { + client->cIsWebsocket = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) { + client->cVersion = headerValue.toInt(); + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) { + client->cKey = headerValue; + client->cKey.trim(); // see rfc6455 + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) { + client->cProtocol = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) { + client->cExtensions = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) { + client->base64Authorization = headerValue; + } else { + client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue); + if(_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) { + client->cMandatoryHeadersCount++; + } + } + + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); + } + + (*headerLine) = ""; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); +#endif + } else { + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsWebsocket: %d\n", client->num, client->cIsWebsocket); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cKey: %s\n", client->num, client->cKey.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cProtocol: %s\n", client->num, client->cProtocol.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cExtensions: %s\n", client->num, client->cExtensions.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cVersion: %d\n", client->num, client->cVersion); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - base64Authorization: %s\n", client->num, client->base64Authorization.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cHttpHeadersValid: %d\n", client->num, client->cHttpHeadersValid); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cMandatoryHeadersCount: %d\n", client->num, client->cMandatoryHeadersCount); + + bool ok = (client->cIsUpgrade && client->cIsWebsocket); + + if(ok) { + if(client->cUrl.length() == 0) { + ok = false; + } + if(client->cKey.length() == 0) { + ok = false; + } + if(client->cVersion != 13) { + ok = false; + } + if(!client->cHttpHeadersValid) { + ok = false; + } + if (client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) { + ok = false; + } + } + + if(_base64Authorization.length() > 0) { + String auth = WEBSOCKETS_STRING("Basic "); + auth += _base64Authorization; + if(auth != client->base64Authorization) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num); + handleAuthorizationFailed(client); + return; + } + } + + if(ok) { + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num); + + // generate Sec-WebSocket-Accept key + String sKey = acceptKey(client->cKey); + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - sKey: %s\n", client->num, sKey.c_str()); + + client->status = WSC_CONNECTED; + + String handshake = WEBSOCKETS_STRING("HTTP/1.1 101 Switching Protocols\r\n" + "Server: arduino-WebSocketsServer\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Accept: "); + handshake += sKey + NEW_LINE; + + if(_origin.length() > 0) { + handshake += WEBSOCKETS_STRING("Access-Control-Allow-Origin: "); + handshake +=_origin + NEW_LINE; + } + + if(client->cProtocol.length() > 0) { + handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: "); + handshake +=_protocol + NEW_LINE; + } + + // header end + handshake += NEW_LINE; + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] handshake %s", client->num, (uint8_t*)handshake.c_str()); + + write(client, (uint8_t*)handshake.c_str(), handshake.length()); + + headerDone(client); + + // send ping + WebSockets::sendFrame(client, WSop_ping); + + runCbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); + + } else { + handleNonWebsocketConnection(client); + } + } +} + + + diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsServer.h b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsServer.h new file mode 100644 index 0000000..db945a6 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/WebSocketsServer.h @@ -0,0 +1,212 @@ +/** + * @file WebSocketsServer.h + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * 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 WEBSOCKETSSERVER_H_ +#define WEBSOCKETSSERVER_H_ + +#include "WebSockets.h" + +#ifndef WEBSOCKETS_SERVER_CLIENT_MAX +#define WEBSOCKETS_SERVER_CLIENT_MAX (5) +#endif + + + + +class WebSocketsServer: protected WebSockets { +public: + +#ifdef __AVR__ + typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length); + typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue); +#else + typedef std::function WebSocketServerEvent; + typedef std::function WebSocketServerHttpHeaderValFunc; +#endif + + WebSocketsServer(uint16_t port, String origin = "", String protocol = "arduino"); + virtual ~WebSocketsServer(void); + + void begin(void); + void close(void); + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void loop(void); +#else + // Async interface not need a loop call + void loop(void) __attribute__ ((deprecated)) {} +#endif + + void onEvent(WebSocketServerEvent cbEvent); + void onValidateHttpHeader( + WebSocketServerHttpHeaderValFunc validationFunc, + const char* mandatoryHttpHeaders[], + size_t mandatoryHttpHeaderCount); + + + bool sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0); + bool sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(uint8_t num, const char * payload, size_t length = 0); + bool sendTXT(uint8_t num, String & payload); + + bool broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool broadcastTXT(const uint8_t * payload, size_t length = 0); + bool broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false); + bool broadcastTXT(const char * payload, size_t length = 0); + bool broadcastTXT(String & payload); + + bool sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload = false); + bool sendBIN(uint8_t num, const uint8_t * payload, size_t length); + + bool broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false); + bool broadcastBIN(const uint8_t * payload, size_t length); + + bool sendPing(uint8_t num, uint8_t * payload = NULL, size_t length = 0); + bool sendPing(uint8_t num, String & payload); + + bool broadcastPing(uint8_t * payload = NULL, size_t length = 0); + bool broadcastPing(String & payload); + + void disconnect(void); + void disconnect(uint8_t num); + + void setAuthorization(const char * user, const char * password); + void setAuthorization(const char * auth); + + int connectedClients(bool ping = false); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + IPAddress remoteIP(uint8_t num); +#endif + +protected: + uint16_t _port; + String _origin; + String _protocol; + String _base64Authorization; ///< Base64 encoded Auth request + String * _mandatoryHttpHeaders; + size_t _mandatoryHttpHeaderCount; + + WEBSOCKETS_NETWORK_SERVER_CLASS * _server; + + WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX]; + + WebSocketServerEvent _cbEvent; + WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc; + + bool _runnning; + + bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); + + void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); + + void clientDisconnect(WSclient_t * client); + bool clientIsConnected(WSclient_t * client); + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void handleNewClients(void); + void handleClientData(void); +#endif + + void handleHeader(WSclient_t * client, String * headerLine); + + /** + * called if a non Websocket connection is coming in. + * Note: can be override + * @param client WSclient_t * ptr to the client struct + */ + virtual void handleNonWebsocketConnection(WSclient_t * client) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num); + client->tcp->write("HTTP/1.1 400 Bad Request\r\n" + "Server: arduino-WebSocket-Server\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 32\r\n" + "Connection: close\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n" + "This is a Websocket server only!"); + clientDisconnect(client); + } + + /** + * called if a non Authorization connection is coming in. + * Note: can be override + * @param client WSclient_t * ptr to the client struct + */ + virtual void handleAuthorizationFailed(WSclient_t *client) { + client->tcp->write("HTTP/1.1 401 Unauthorized\r\n" + "Server: arduino-WebSocket-Server\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 45\r\n" + "Connection: close\r\n" + "Sec-WebSocket-Version: 13\r\n" + "WWW-Authenticate: Basic realm=\"WebSocket Server\"" + "\r\n" + "This Websocket server requires Authorization!"); + clientDisconnect(client); + } + + /** + * called for sending a Event to the app + * @param num uint8_t + * @param type WStype_t + * @param payload uint8_t * + * @param length size_t + */ + virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + if(_cbEvent) { + _cbEvent(num, type, payload, length); + } + } + + /* + * Called at client socket connect handshake negotiation time for each http header that is not + * a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*) + * If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the + * socket negotiation is considered invalid and the upgrade to websockets request is denied / rejected + * This mechanism can be used to enable custom authentication schemes e.g. test the value + * of a session cookie to determine if a user is logged on / authenticated + */ + virtual bool execHttpHeaderValidation(String headerName, String headerValue) { + if(_httpHeaderValidationFunc) { + //return the value of the custom http header validation function + return _httpHeaderValidationFunc(headerName, headerValue); + } + //no custom http header validation so just assume all is good + return true; + } + +private: + /* + * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection + * @param headerName String ///< the name of the header being checked + */ + bool hasMandatoryHeader(String headerName); + +}; + + + +#endif /* WEBSOCKETSSERVER_H_ */ diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/AUTHORS b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/AUTHORS new file mode 100644 index 0000000..af68737 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/AUTHORS @@ -0,0 +1,7 @@ +libb64: Base64 Encoding/Decoding Routines +====================================== + +Authors: +------- + +Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/LICENSE b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/LICENSE new file mode 100644 index 0000000..a6b5606 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/LICENSE @@ -0,0 +1,29 @@ +Copyright-Only Dedication (based on United States law) +or Public Domain Certification + +The person or persons who have associated work with this document (the +"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of +his knowledge, the work of authorship identified is in the public domain of the +country from which the work is published, or (b) hereby dedicates whatever +copyright the dedicators holds in the work of authorship identified below (the +"Work") to the public domain. A certifier, moreover, dedicates any copyright +interest he may have in the associated work, and for these purposes, is +described as a "dedicator" below. + +A certifier has taken reasonable steps to verify the copyright status of this +work. Certifier recognizes that his good faith efforts may not shield him from +liability if in fact the work certified is not in the public domain. + +Dedicator makes this dedication for the benefit of the public at large and to +the detriment of the Dedicator's heirs and successors. Dedicator intends this +dedication to be an overt act of relinquishment in perpetuity of all present +and future rights under copyright law, whether vested or contingent, in the +Work. Dedicator understands that such relinquishment of all rights includes +the relinquishment of all rights to enforce (by lawsuit or otherwise) those +copyrights in the Work. + +Dedicator recognizes that, once placed in the public domain, the Work may be +freely reproduced, distributed, transmitted, used, modified, built upon, or +otherwise exploited by anyone for any purpose, commercial or non-commercial, +and in any way, including by methods that have not yet been invented or +conceived. \ No newline at end of file diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cdecode.c b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cdecode.c new file mode 100644 index 0000000..e135da2 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cdecode.c @@ -0,0 +1,98 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifdef ESP8266 +#include +#endif + +#if defined(ESP32) +#define CORE_HAS_LIBB64 +#endif + +#ifndef CORE_HAS_LIBB64 +#include "cdecode_inc.h" + +int base64_decode_value(char value_in) +{ + static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + static const char decoding_size = sizeof(decoding); + value_in -= 43; + if (value_in < 0 || value_in > decoding_size) return -1; + return decoding[(int)value_in]; +} + +void base64_init_decodestate(base64_decodestate* state_in) +{ + state_in->step = step_a; + state_in->plainchar = 0; +} + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) +{ + const char* codechar = code_in; + char* plainchar = plaintext_out; + char fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step) + { + while (1) + { + case step_a: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + case step_b: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; +} + +#endif diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cdecode_inc.h b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cdecode_inc.h new file mode 100644 index 0000000..d0d7f48 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cdecode_inc.h @@ -0,0 +1,28 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +typedef enum +{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct +{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in); + +#endif /* BASE64_CDECODE_H */ diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cencode.c b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cencode.c new file mode 100644 index 0000000..afe1463 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cencode.c @@ -0,0 +1,119 @@ +/* +cencoder.c - c source to a base64 encoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifdef ESP8266 +#include +#endif + +#if defined(ESP32) +#define CORE_HAS_LIBB64 +#endif + +#ifndef CORE_HAS_LIBB64 +#include "cencode_inc.h" + +const int CHARS_PER_LINE = 72; + +void base64_init_encodestate(base64_encodestate* state_in) +{ + state_in->step = step_A; + state_in->result = 0; + state_in->stepcount = 0; +} + +char base64_encode_value(char value_in) +{ + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) return '='; + return encoding[(int)value_in]; +} + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) +{ + const char* plainchar = plaintext_in; + const char* const plaintextend = plaintext_in + length_in; + char* codechar = code_out; + char result; + char fragment; + + result = state_in->result; + + switch (state_in->step) + { + while (1) + { + case step_A: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_A; + return codechar - code_out; + } + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + case step_B: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_B; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + case step_C: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_C; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + + ++(state_in->stepcount); + if (state_in->stepcount == CHARS_PER_LINE/4) + { + *codechar++ = '\n'; + state_in->stepcount = 0; + } + } + } + /* control should not reach here */ + return codechar - code_out; +} + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in) +{ + char* codechar = code_out; + + switch (state_in->step) + { + case step_B: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + *codechar++ = '='; + break; + case step_C: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + break; + case step_A: + break; + } + *codechar++ = 0x00; + + return codechar - code_out; +} + +#endif diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cencode_inc.h b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cencode_inc.h new file mode 100644 index 0000000..c1e3464 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libb64/cencode_inc.h @@ -0,0 +1,31 @@ +/* +cencode.h - c header for a base64 encoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CENCODE_H +#define BASE64_CENCODE_H + +typedef enum +{ + step_A, step_B, step_C +} base64_encodestep; + +typedef struct +{ + base64_encodestep step; + char result; + int stepcount; +} base64_encodestate; + +void base64_init_encodestate(base64_encodestate* state_in); + +char base64_encode_value(char value_in); + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in); + +#endif /* BASE64_CENCODE_H */ diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libsha1/libsha1.c b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libsha1/libsha1.c new file mode 100644 index 0000000..48f4df5 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libsha1/libsha1.c @@ -0,0 +1,202 @@ +/* from valgrind tests */ + +/* ================ sha1.c ================ */ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#if !defined(ESP8266) && !defined(ESP32) + +#define SHA1HANDSOFF + +#include +#include +#include + +#include "libsha1.h" + + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#elif BYTE_ORDER == BIG_ENDIAN +#define blk0(i) block->l[i] +#else +#error "Endianness not defined!" +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union { + unsigned char c[64]; + uint32_t l[16]; + } CHAR64LONG16; +#ifdef SHA1HANDSOFF + CHAR64LONG16 block[1]; /* use array to appear as a pointer */ + memcpy(block, buffer, 64); +#else + /* The following had better never be used because it causes the + * pointer-to-const buffer to be cast into a pointer to non-const. + * And the result is written through. I threw a "const" in, hoping + * this will cause a diagnostic. + */ + CHAR64LONG16* block = (const CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + memset(block, '\0', sizeof(block)); +#endif +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len) +{ + uint32_t i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ + unsigned i; + unsigned char finalcount[8]; + unsigned char c; + +#if 0 /* untested "improvement" by DHR */ + /* Convert context->count to a sequence of bytes + * in finalcount. Second element first, but + * big-endian order within element. + * But we do it all backwards. + */ + unsigned char *fcp = &finalcount[8]; + + for (i = 0; i < 2; i++) + { + uint32_t t = context->count[i]; + int j; + + for (j = 0; j < 4; t >>= 8, j++) + *--fcp = (unsigned char) t; + } +#else + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } +#endif + c = 0200; + SHA1Update(context, &c, 1); + while ((context->count[0] & 504) != 448) { + c = 0000; + SHA1Update(context, &c, 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); +} +/* ================ end of sha1.c ================ */ + + +#endif diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libsha1/libsha1.h b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libsha1/libsha1.h new file mode 100644 index 0000000..ee3718e --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/src/libsha1/libsha1.h @@ -0,0 +1,21 @@ +/* ================ sha1.h ================ */ +/* +SHA-1 in C +By Steve Reid +100% Public Domain +*/ + +#if !defined(ESP8266) && !defined(ESP32) + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); +void SHA1Final(unsigned char digest[20], SHA1_CTX* context); + +#endif diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/tests/webSocket.html b/Grbl_Esp32-master/libraries/arduinoWebSockets/tests/webSocket.html new file mode 100644 index 0000000..66a2708 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/tests/webSocket.html @@ -0,0 +1,49 @@ + + + + + + + +LED Control:
+
+R:
+G:
+B:
+ + \ No newline at end of file diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/tests/webSocketServer/index.js b/Grbl_Esp32-master/libraries/arduinoWebSockets/tests/webSocketServer/index.js new file mode 100644 index 0000000..389e193 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/tests/webSocketServer/index.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node +var WebSocketServer = require('websocket').server; +var http = require('http'); + +var server = http.createServer(function(request, response) { + console.log((new Date()) + ' Received request for ' + request.url); + response.writeHead(404); + response.end(); +}); +server.listen(8011, function() { + console.log((new Date()) + ' Server is listening on port 8011'); +}); + +wsServer = new WebSocketServer({ + httpServer: server, + // You should not use autoAcceptConnections for production + // applications, as it defeats all standard cross-origin protection + // facilities built into the protocol and the browser. You should + // *always* verify the connection's origin and decide whether or not + // to accept it. + autoAcceptConnections: false +}); + +function originIsAllowed(origin) { + // put logic here to detect whether the specified origin is allowed. + return true; +} + +wsServer.on('request', function(request) { + + if (!originIsAllowed(request.origin)) { + // Make sure we only accept requests from an allowed origin + request.reject(); + console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.'); + return; + } + + var connection = request.accept('arduino', request.origin); + console.log((new Date()) + ' Connection accepted.'); + + connection.on('message', function(message) { + if (message.type === 'utf8') { + console.log('Received Message: ' + message.utf8Data); + // connection.sendUTF(message.utf8Data); + } + else if (message.type === 'binary') { + console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); + //connection.sendBytes(message.binaryData); + } + }); + + connection.on('close', function(reasonCode, description) { + console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.'); + }); + + connection.sendUTF("Hallo Client!"); +}); diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/tests/webSocketServer/package.json b/Grbl_Esp32-master/libraries/arduinoWebSockets/tests/webSocketServer/package.json new file mode 100644 index 0000000..9538323 --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/tests/webSocketServer/package.json @@ -0,0 +1,27 @@ +{ + "name": "webSocketServer", + "version": "1.0.0", + "description": "WebSocketServer for testing", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/Links2004/arduinoWebSockets" + }, + "keywords": [ + "esp8266", + "websocket", + "arduino" + ], + "author": "Markus Sattler", + "license": "LGPLv2", + "bugs": { + "url": "https://github.com/Links2004/arduinoWebSockets/issues" + }, + "homepage": "https://github.com/Links2004/arduinoWebSockets", + "dependencies": { + "websocket": "^1.0.18" + } +} diff --git a/Grbl_Esp32-master/libraries/arduinoWebSockets/travis/common.sh b/Grbl_Esp32-master/libraries/arduinoWebSockets/travis/common.sh new file mode 100644 index 0000000..be959fa --- /dev/null +++ b/Grbl_Esp32-master/libraries/arduinoWebSockets/travis/common.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +function build_sketches() +{ + local arduino=$1 + local srcpath=$2 + local platform=$3 + local sketches=$(find $srcpath -name *.ino) + for sketch in $sketches; do + local sketchdir=$(dirname $sketch) + if [[ -f "$sketchdir/.$platform.skip" ]]; then + echo -e "\n\n ------------ Skipping $sketch ------------ \n\n"; + continue + fi + echo -e "\n\n ------------ Building $sketch ------------ \n\n"; + $arduino --verify $sketch; + local result=$? + if [ $result -ne 0 ]; then + echo "Build failed ($sketch) build verbose..." + $arduino --verify --verbose --preserve-temp-files $sketch + result=$? + fi + if [ $result -ne 0 ]; then + echo "Build failed ($1) $sketch" + return $result + fi + done +} + + +function get_core() +{ + echo Setup core for $1 + + cd $HOME/arduino_ide/hardware + + if [ "$1" = "esp8266" ] ; then + mkdir esp8266com + cd esp8266com + git clone https://github.com/esp8266/Arduino.git esp8266 + cd esp8266/tools + python get.py + fi + + if [ "$1" = "esp32" ] ; then + mkdir espressif + cd espressif + git clone https://github.com/espressif/arduino-esp32.git esp32 + cd esp32/tools + python get.py + fi + +} diff --git a/Grbl_Esp32-master/platformio.ini b/Grbl_Esp32-master/platformio.ini new file mode 100644 index 0000000..0ca16f2 --- /dev/null +++ b/Grbl_Esp32-master/platformio.ini @@ -0,0 +1,29 @@ +[platformio] +src_dir=Grbl_Esp32 +lib_dir=libraries +data_dir=Grbl_Esp32/data + +[common_env_data] +lib_deps_builtin = + ArduinoOTA + BluetoothSerial + DNSServer + EEPROM + ESPmDNS + FS + Preferences + SD + SPI + SPIFFS + Update + WebServer + WiFi + WiFiClientSecure + +[env:nodemcu-32s] +platform = espressif32 +board = nodemcu-32s +framework = arduino +upload_speed = 512000 +board_build.partitions = min_spiffs.csv +monitor_speed = 115200 diff --git a/Ressource/README.md b/Ressource/README.md index 5b8e1a5..3a2d566 100644 --- a/Ressource/README.md +++ b/Ressource/README.md @@ -54,8 +54,6 @@ M5 = spindle off M3 = spindle on -Spindle gpio 22 sur l'esp32 boolean 0-3.3v - Setup le G28 (position safe de la machine) En envoyant ? ou en activant le verbose mode grbl report status. @@ -67,3 +65,86 @@ Commande de job envoyé par l'interface web $J=G91 G21 F1000 X-10 + +###CONFIG GRBL_ESP32 CRA4: +- Augmenter le temps de démarrage de la spindle à 3-4sec +- Activer la carte sd OK +- Ne pas ignorer les pin de contrôle qui sur un esp seul n'ont pas de pullup et pose problème contrairement à ici. Cycle Start | Feed Hold | Reset | Safety Door OK +- Inverser la logique de commande de la spindle. 3.3vHIGH = off 0vLOW = on OK +- Possibilité de desactivé la pwm sur la spindle qui n'est ici pas utilisé +- Augmenter le BLOCK_BUFFER_SIZE et RX_BUFFER_SIZE OK +- Comme les capas de filtrages et les résistances de pullup suffisent pas activer ENABLE_SOFTWARE_DEBOUNCE (faudrait des optos coupleurs normalement et des endstop en >12v au lieu de 3.3v) OK + +###PINOUT GRBL_ESP32 CRA4 (v3.5) +Stepper disable sur GPIO 2 (c'est celle ou il y a led bleu relié sur l'esp car si utilisé en input il y a des problèmes) +X_LIMIT_PIN GPIO_NUM_13 + +#ifdef CPU_MAP_ESP32 + // This is the CPU Map for the ESP32 CNC Controller R2 + + // It is OK to comment out any step and direction pins. This + // won't affect operation except that there will be no output + // form the pins. Grbl will virtually move the axis. This could + // be handy if you are using a servo, etc. for another axis. + #define CPU_MAP_NAME "CPU_MAP_ESP32" + + #define X_STEP_PIN GPIO_NUM_12 + #define X_DIRECTION_PIN GPIO_NUM_26 + #define X_RMT_CHANNEL 0 + + #define Y_STEP_PIN GPIO_NUM_14 + #define Y_DIRECTION_PIN GPIO_NUM_25 + #define Y_RMT_CHANNEL 1 + + #define Z_STEP_PIN GPIO_NUM_27 + #define Z_DIRECTION_PIN GPIO_NUM_33 + #define Z_RMT_CHANNEL 2 + + // OK to comment out to use pin for other features + #define STEPPERS_DISABLE_PIN GPIO_NUM_2 + + // *** the flood coolant feature code is activated by defining this pins + // *** Comment it out to use the pin for other features + #define COOLANT_FLOOD_PIN GPIO_NUM_16 + //#define COOLANT_MIST_PIN GPIO_NUM_21 + + // If SPINDLE_PWM_PIN is commented out, this frees up the pin, but Grbl will still + // use a virtual spindle. Do not comment out the other parameters for the spindle. + #define SPINDLE_PWM_PIN GPIO_NUM_17 + #define SPINDLE_PWM_CHANNEL 0 + // 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) + #define SPINDLE_PWM_BASE_FREQ 5000 // Hz + #define SPINDLE_PWM_BIT_PRECISION 8 // be sure to match this with SPINDLE_PWM_MAX_VALUE + #define SPINDLE_PWM_OFF_VALUE 0 + #define SPINDLE_PWM_MAX_VALUE 255 // (2^SPINDLE_PWM_BIT_PRECISION) + + #ifndef SPINDLE_PWM_MIN_VALUE + #define SPINDLE_PWM_MIN_VALUE 1 // Must be greater than zero. + #endif + + #define SPINDLE_ENABLE_PIN GPIO_NUM_22 + + #define SPINDLE_PWM_RANGE (SPINDLE_PWM_MAX_VALUE-SPINDLE_PWM_MIN_VALUE) + + // if these spindle function pins are defined, they will be activated in the code + // comment them out to use the pins for other functions + //#define SPINDLE_ENABLE_PIN GPIO_NUM_16 + //#define SPINDLE_DIR_PIN GPIO_NUM_16 + + #define X_LIMIT_PIN GPIO_NUM_13 + #define Y_LIMIT_PIN GPIO_NUM_4 + #define Z_LIMIT_PIN GPIO_NUM_15 + #define LIMIT_MASK B111 + + #define PROBE_PIN GPIO_NUM_32 + + #define CONTROL_SAFETY_DOOR_PIN GPIO_NUM_35 // needs external pullup + #define CONTROL_RESET_PIN GPIO_NUM_34 // needs external pullup + #define CONTROL_FEED_HOLD_PIN GPIO_NUM_36 // needs external pullup + #define CONTROL_CYCLE_START_PIN GPIO_NUM_39 // needs external pullup + +#endif diff --git a/Ressource/configGRBL_Probe.txt b/Ressource/configGRBL.txt similarity index 89% rename from Ressource/configGRBL_Probe.txt rename to Ressource/configGRBL.txt index 071ae66..405acdc 100644 --- a/Ressource/configGRBL_Probe.txt +++ b/Ressource/configGRBL.txt @@ -1,4 +1,18 @@ -$0=10 (Step pulse time, microseconds) +Probe cncjs +; Z-Probe v2 +G91 +G38.2 Z-10 F10 ; Vitesse de descente 10, descente de max 10mm +G90 +; Set the active WCS Z0 +G10 L20 P1 Z0 ; Touch plate sans epaisseur +; Retract from the touch plate +G91 +G28 G91 Z0 ; Le z d'abord +G28 ; Go G28 +G90 + + +$0=12 (Step pulse time, microseconds) $1=250 (Step idle delay, milliseconds) $2=0 (Step pulse invert, mask) $3=0 (Step direction invert, mask) @@ -27,21 +41,8 @@ $110=5000.000 (X-axis maximum rate, mm/min) $111=6000.000 (Y-axis maximum rate, mm/min) $112=6000.000 (Z-axis maximum rate, mm/min) $120=200.000 (X-axis acceleration, mm/sec^2) -$121=200.000 (Y-axis acceleration, mm/sec^2) +$121=250.000 (Y-axis acceleration, mm/sec^2) $122=200.000 (Z-axis acceleration, mm/sec^2) $130=300.000 (X-axis maximum travel, millimeters) $131=210.000 (Y-axis maximum travel, millimeters) -$132=100.000 (Z-axis maximum travel, mi - -Probe cncjs -; Z-Probe v2 -G91 -G38.2 Z-10 F10 ; Vitesse de descente 10, descente de max 10mm -G90 -; Set the active WCS Z0 -G10 L20 P1 Z0 ; Touch plate sans epaisseur -; Retract from the touch plate -G91 -G28 G91 Z0 ; Le z d'abord -G28 ; Go G28 -G90 \ No newline at end of file +$132=100.000 (Z-axis maximum travel, millimeters)