diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b5fb4de5..aee73d91 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,7 @@ +v1.0.379: +- Major improvements to USB connection steps, +- set up parser before checking for firmware +- Added more grblHAL errors/settings descriptions v1.0.378: - Test fix for 4X hanging on DTR connect v1.0.377: diff --git a/app/js/grbl-settings-templates.js b/app/js/grbl-settings-templates.js index 8405e8a8..40014356 100644 --- a/app/js/grbl-settings-templates.js +++ b/app/js/grbl-settings-templates.js @@ -885,5 +885,20 @@ var grblSettingsTemplate2 = { description: `Auto mount SD card on startup. 0 - Auto mount SD card (1) / 1 - Hide LittleFS (2)`, template: ``, utils: `` + }, + 328: { + key: `$328`, + title: `FTP port, range: 1 - 65535, reboot required`, + description: `FTP port number listening for incoming connections. NOTE: A hard reset of the controller is required after changing this setting.`, + template: ``, + utils: `` + }, + 372: { + key: `$372`, + title: `Invert I/O Port outputs as bitfield`, + description: ` Invert I/O Port outputs as bitfield`, + template: ``, + utils: `` } + } \ No newline at end of file diff --git a/app/js/websocket.js b/app/js/websocket.js index ebf4d867..02691d3c 100644 --- a/app/js/websocket.js +++ b/app/js/websocket.js @@ -1224,7 +1224,7 @@ function friendlyPort(i) { if (laststatus.comms.interfaces.ports[i].productId == '6015' && laststatus.comms.interfaces.ports[i].vendorId == '1D50') { // found Smoothieboard img = 'smoothieboard.png'; - note = 'Smoothieware USB Port'; + note = 'Smoothieware USB Port (Not Supported)'; } if (laststatus.comms.interfaces.ports[i].productId == '6001' && laststatus.comms.interfaces.ports[i].vendorId == '0403') { // found FTDI FT232 @@ -1259,7 +1259,7 @@ function friendlyPort(i) { if (laststatus.comms.interfaces.ports[i].productId == '7523' && laststatus.comms.interfaces.ports[i].vendorId == '1A86') { // found CH340 img = 'uno.png'; - note = 'CH340 Arduino Fake'; + note = 'WCH.cn CH340 USB to UART'; } if (laststatus.comms.interfaces.ports[i].productId == 'EA60' && laststatus.comms.interfaces.ports[i].vendorId == '10C4') { // found CP2102 @@ -1269,7 +1269,7 @@ function friendlyPort(i) { if (laststatus.comms.interfaces.ports[i].productId == '000A' && laststatus.comms.interfaces.ports[i].vendorId == '2E8A') { // found CP2102 img = 'pipico.png'; - note = 'Raspberry Pi Pico CDC AURT'; + note = 'Raspberry Pi Pico CDC UART (Not Supported)'; } if (laststatus.comms.interfaces.ports[i].productId == '2303' && laststatus.comms.interfaces.ports[i].vendorId == '067B') { // found CP2102 diff --git a/grblStrings.js b/grblStrings.js index e7b57996..4f7882c9 100644 --- a/grblStrings.js +++ b/grblStrings.js @@ -2,44 +2,74 @@ var exports = module.exports = {}; var grblErrorCodes = { - 0: "no error", + 0: "No error", 1: "G-code words consist of a letter and a value. Letter was not found.", - 2: "Numeric value format is not valid or missing an expected value.", - 3: "Grbl '$' system command was not recognized or supported.", + 2: "Missing the expected G-code word value or numeric value format is not valid.", + 3: "'$' system command was not recognized or supported.", 4: "Negative value received for an expected positive value.", - 5: "Homing cycle is not enabled via settings.", - 6: "Minimum step pulse time must be greater than 3usec", - 7: "EEPROM read failed. Reset and restored to default values.", - 8: "Grbl '$' command cannot be used unless Grbl is IDLE. Ensures smooth operation during a job.", - 9: "G-code locked out during alarm or jog state", + 5: "Homing cycle failure. Homing is not configured via settings.", + 6: "Step pulse time must be greater or equal to 2 microseconds.", + 7: "A settings read failed. Auto-restoring affected settings to default values.", + 8: "'$' command cannot be used unless controller state is IDLE. Ensures smooth operation during a job.", + 9: "G-code commands are locked out during alarm or jog state.", 10: "Soft limits cannot be enabled without homing also enabled.", - 11: "Max characters per line exceeded. Line was not processed and executed.", - 12: "(Compile Option) Grbl '$' setting value exceeds the maximum step rate supported.", + 11: "Max characters per line exceeded. Received command line was not executed.", + 12: "'$' setting value causes the step rate to exceed the maximum supported.", 13: "Safety door detected as opened and door state initiated.", - 14: "(Grbl-Mega Only) Build info or startup line exceeded EEPROM line length limit.", - 15: "Jog target exceeds machine travel. Command ignored.", - 16: "Jog command with no '=' or contains prohibited g-code.", - 20: "Unsupported or invalid g-code command found in block.", - 21: "More than one g-code command from same modal group found in block.", + 14: "Build info or startup line exceeded line length limit. Line not stored.", + 15: "Jog target exceeds machine travel. Jog command has been ignored.", + 16: "Jog command has no '=' or contains prohibited G-code.", + 17: "Laser mode requires PWM output.", + 18: "Reset asserted.", + 19: "Non positive value.", + 20: "Unsupported or invalid G-code command found in block.", + 21: "More than one G-code command from same modal group found in block.", 22: "Feed rate has not yet been set or is undefined.", 23: "G-code command in block requires an integer value.", - 24: "Two G-code commands that both require the use of the XYZ axis words were detected in the block.", - 25: "A G-code word was repeated in the block.", - 26: "A G-code command implicitly or explicitly requires XYZ axis words in the block, but none were detected.", - 27: "N line number value is not within the valid range of 1 - 9,999,999.", - 28: "A G-code command was sent, but is missing some required P or L value words in the line.", - 29: "Grbl supports six work coordinate systems G54-G59. G59.1, G59.2, and G59.3 are not supported.", - 30: "The G53 G-code command requires either a G0 seek or G1 feed motion mode to be active. A different motion was active.", - 31: "There are unused axis words in the block and G80 motion mode cancel is active.", - 32: "A G2 or G3 arc was commanded but there are no XYZ axis words in the selected plane to trace the arc.", - 33: "The motion command has an invalid target. G2, G3, and G38.2 generates this error, if the arc is impossible to generate or if the probe target is the current position.", - 34: "A G2 or G3 arc, traced with the radius definition, had a mathematical error when computing the arc geometry. Try either breaking up the arc into semi-circles or quadrants, or redefine them with the arc offset definition.", - 35: "A G2 or G3 arc, traced with the offset definition, is missing the IJK offset word in the selected plane to trace the arc.", - 36: "There are unused, leftover G-code words that aren't used by any command in the block.", - 37: "The G43.1 dynamic tool length offset command cannot apply an offset to an axis other than its configured axis. The Grbl default axis is the Z-axis.", - 46: "Home machine to continue" + 24: "More than one G-code command that requires axis words found in block.", + 25: "Repeated G-code word found in block.", + 26: "No axis words found in block for G-code command or current modal state which requires them.", + 27: "Line number value is invalid.", + 28: "G-code command is missing a required value word.", + 29: "G59.x work coordinate systems are not supported.", + 30: "G53 only allowed with G0 and G1 motion modes.", + 31: "Axis words found in block when no command or current modal state uses them.", + 32: "G2 and G3 arcs require at least one in-plane axis word.", + 33: "Motion command target is invalid.", + 34: "Arc radius value is invalid.", + 35: "G2 and G3 arcs require at least one in-plane offset word.", + 36: "Unused value words found in block.", + 37: "G43.1 dynamic tool length offset is not assigned to configured tool length axis.", + 38: "Tool number greater than max supported value or undefined tool selected.", + 39: "Value out of range.", + 40: "G-code command not allowed when tool change is pending.", + 41: "Spindle not running when motion commanded in CSS or spindle sync mode.", + 42: "Plane must be ZX for threading.", + 43: "Max. feed rate exceeded.", + 44: "RPM out of range.", + 45: "Only homing is allowed when a limit switch is engaged.", + 46: "Home machine to continue.", + 47: "ATC: current tool is not set. Set current tool with M61.", + 48: "Value word conflict.", + 49: "Power on self test failed. A hard reset is required.", + 50: "Emergency stop active.", + 51: "Motor fault.", + 52: "Setting value is out of range.", + 53: "Setting is not available, possibly due to limited driver support.", + 54: "Retract position is less than drill depth.", + 55: "Attempt to home two auto squared axes at the same time.", + 56: "Coordinate system is locked.", + 60: "SD Card mount failed.", + 61: "SD Card file open/read failed.", + 62: "SD Card directory listing failed.", + 63: "SD Card directory not found.", + 64: "SD Card file empty.", + 77: "Authentication required.", + 78: "Access denied.", + 79: "Not allowed while critical event is active." }; + var grblAlarmCodes = { 0: "no alarm", 1: "Hard limit triggered. Machine position is likely lost due to sudden and immediate halt. Re-homing is highly recommended.", @@ -58,7 +88,8 @@ var grblAlarmCodes = { 14: "Spindle at speed timeout. Clear before continuing.", 15: "Homing fail. Could not find second limit switch for auto squared axis within search distances. Try increasing max travel, decreasing pull-off distance, or check wiring.", 16: "Power on selftest (POS) failed.", - 17: "Motor fault." + 17: "Motor fault.", + 18: "Homing fail. Bad configuration." }; var grblSettingCodes = { @@ -157,8 +188,9 @@ var grblSettingCodes = { 484: "Unlock required after E-Stop as boolean", 486: "Lock coordinate systems against accidental changes", 650: "File systems options as bitfield", // Auto mount SD? - 673: "Coolant on delay in s after Feedhold resume" - + 673: "Coolant on delay in s after Feedhold resume", + 328: "FTP port, range: 1 - 65535, reboot required", + 372: "Invert I/O Port outputs as bitfield:" }; exports.errors = function(id) { diff --git a/index.js b/index.js index c3bf5e8c..ae653b01 100644 --- a/index.js +++ b/index.js @@ -60,7 +60,7 @@ config.nextWebPort = function() { config.webPort = process.env.WEB_PORT || config.nextWebPort(); config.posDecimals = process.env.DRO_DECIMALS || 3; config.grblWaitTime = 0.5; -config.firmwareWaitTime = 4; + var express = require("express"); var app = express(); @@ -1205,8 +1205,386 @@ io.on("connection", function(socket) { stopPort() // also clear queues etc }); // end port.onclose + function portOpened(port, data) { + // setup listeners first + + parser.on("data", function(data) { + //console.log(data) + var command = sentBuffer[0]; + + if (command == "$CD" && data != "ok") { + fluidncConfig = fluidncConfig += data + "\n" + } + + if (data.indexOf("<") != 0) { + debug_log('data:', data) + } + + // Grbl $I parser + if (data.indexOf("[VER:") === 0) { + status.machine.name = data.split(':')[2].split(']')[0].toLowerCase() + io.sockets.emit("status", status); + io.sockets.emit("machinename", data.split(':')[2].split(']')[0].toLowerCase()); + status.machine.firmware.date = data.split(':')[1].split(".")[2]; + } + + if (data.indexOf("[OPT:") === 0) { + + var startOpt = data.search(/opt:/i) + 4; + var grblOpt; + if (startOpt > 4) { + var grblOptLen = data.substr(startOpt).search(/]/); + grblOpts = data.substr(startOpt, grblOptLen).split(/,/); + + status.machine.firmware.blockBufferSize = grblOpts[1]; + status.machine.firmware.rxBufferSize = grblOpts[2]; + + var features = [] + + var i = grblOpts[0].length; + while (i--) { + features.push(grblOpts[0].charAt(i)) + switch (grblOpts[0].charAt(i)) { + case 'Q': + debug_log('SPINDLE_IS_SERVO Enabled') + // + break; + case 'V': // Variable spindle enabled + debug_log('Variable spindle enabled') + // + break; + case 'N': // Line numbers enabled + debug_log('Line numbers enabled') + // + break; + case 'M': // Mist coolant enabled + debug_log('Mist coolant enabled') + // + break; + case 'C': // CoreXY enabled + debug_log('CoreXY enabled') + // + break; + case 'P': // Parking motion enabled + debug_log('Parking motion enabled') + // + break; + case 'Z': // Homing force origin enabled + debug_log('Homing force origin enabled') + // + break; + case 'H': // Homing single axis enabled + debug_log('Homing single axis enabled') + // + break; + case 'T': // Two limit switches on axis enabled + debug_log('Two limit switches on axis enabled') + // + break; + case 'A': // Allow feed rate overrides in probe cycles + debug_log('Allow feed rate overrides in probe cycles') + // + break; + case '$': // Restore EEPROM $ settings disabled + debug_log('Restore EEPROM $ settings disabled') + // + break; + case '#': // Restore EEPROM parameter data disabled + debug_log('Restore EEPROM parameter data disabled') + // + break; + case 'I': // Build info write user string disabled + debug_log('Build info write user string disabled') + // + break; + case 'E': // Force sync upon EEPROM write disabled + debug_log('Force sync upon EEPROM write disabled') + // + break; + case 'W': // Force sync upon work coordinate offset change disabled + debug_log('Force sync upon work coordinate offset change disabled') + // + break; + case 'L': // Homing init lock sets Grbl into an alarm state upon power up + debug_log('Homing init lock sets Grbl into an alarm state upon power up') + // + break; + } + } + status.machine.firmware.features = features; + io.sockets.emit("features", features); + } + } + + // [PRB:0.000,0.000,0.000:0] + //if (data.indexOf("[PRB:") === 0 && command != "$#" && command != undefined) { + if (data.indexOf("[PRB:") === 0) { + debug_log(data) + var prbLen = data.substr(5).search(/\]/); + var prbData = data.substr(5, prbLen).split(/,/); + var success = data.split(':')[2].split(']')[0]; + status.machine.probe.x = prbData[0]; + status.machine.probe.y = prbData[1]; + status.machine.probe.z = prbData[2].split(':')[0]; + status.machine.probe.state = success; + if (success > 0) { + var output = { + 'command': '[ PROBE ]', + 'response': "Probe Completed.", + 'type': 'success' + } + io.sockets.emit('data', output); + } else { + var output = { + 'command': '[ PROBE ]', + 'response': "Probe move ERROR - probe did not make contact within specified distance", + 'type': 'error' + } + io.sockets.emit('data', output); + } + io.sockets.emit('prbResult', status.machine.probe); + }; + + if (data.indexOf("[GC:") === 0) { + gotModals(data) + } + + if (data.indexOf("[INTF:") === 0) { + var output = { + 'command': 'connect', + 'response': "Detected an OpenBuilds Interface on port " + port.path, + 'type': 'success' + } + io.sockets.emit('data', output); + status.interface.connected = true; + if (data.split(":")[1].indexOf("ver") == 0) { + var installedVersion = parseFloat(data.split(":")[1].split("]")[0].split("-")[1]) + status.interface.firmware.installedVersion = installedVersion + var output = { + 'command': 'connect', + 'response': "OpenBuilds Interface Firmware Version: v" + installedVersion, + 'type': 'info' + } + io.sockets.emit('data', output); + if (installedVersion < status.interface.firmware.availVersion) { + var output = { + 'command': 'connect', + 'response': "OpenBuilds Interface Firmware OUTDATED: v" + installedVersion + " can be upgraded to v" + status.interface.firmware.availVersion, + 'type': 'error' + } + io.sockets.emit('data', output); + io.sockets.emit('interfaceOutdated', status); + } + } + io.sockets.emit("status", status); + } + + // Machine Identification + if (data.indexOf("Grbl") === 0 || data.indexOf("[FIRMWARE:grblHAL]") === 0) { // Check if it's Grbl + debug_log(data) + status.comms.blocked = false; + if (data.indexOf("GrblHAL") === 0) { + status.machine.firmware.type = "grbl"; + status.machine.firmware.platform = "grblHAL" + status.machine.firmware.version = data.substr(8, 4); // get version + } else if (data.indexOf("[FIRMWARE:grblHAL]") === 0) { + status.machine.firmware.type = "grbl"; + status.machine.firmware.platform = "grblHAL" + // Parse version from seperate [VER:...] line not here for this response + } else if (data.indexOf("FluidNC") != -1) { // Grbl 3.6 [FluidNC v3.6.5 (wifi) '$' for help] + status.machine.firmware.type = "grbl"; + status.machine.firmware.platform = "FluidNC" + status.machine.firmware.version = data.substr(19, 5); // get version + } else { + status.machine.firmware.type = "grbl"; + status.machine.firmware.platform = "gnea" + status.machine.firmware.version = data.substr(5, 4); // get version + } + if (parseFloat(status.machine.firmware.version) < 1.1) { // If version is too old + if (status.machine.firmware.version.length < 3) { + debug_log('invalid version string, stay connected') + } else { + if (status.comms.connectionStatus > 0) { + debug_log('WARN: Closing Port ' + port.path + " / v" + parseFloat(status.machine.firmware.version)); + // stopPort(); + } else { + debug_log('ERROR: Machine connection not open!'); + } + var output = { + 'command': command, + 'response': "Detected an unsupported version: Grbl " + status.machine.firmware.version + ". This is sadly outdated. Please upgrade to Grbl 1.1 or newer to use this software. Go to http://github.com/gnea/grbl", + 'type': 'error' + } + io.sockets.emit('data', output); + } + } + status.machine.firmware.date = ""; + // debug_log("GRBL detected"); + // setTimeout(function() { + // io.sockets.emit('grbl', status.machine.firmware) + // //v1.0.318 - commented out as a test - too many normal alarms clear prematurely + // //io.sockets.emit('errorsCleared', true); + // }, 600) + // // Start interval for status queries + // clearInterval(statusLoop); + // statusLoop = setInterval(function() { + // if (status.comms.connectionStatus > 0) { + // addQRealtime("?"); + // } + // }, 200); + status.machine.modals.homedRecently = false; + } else if (data.indexOf("LPC176") >= 0) { // LPC1768 or LPC1769 should be Smoothieware + status.comms.blocked = false; + debug_log("Smoothieware detected"); + status.machine.firmware.type = "smoothie"; + status.machine.firmware.version = data.substr(data.search(/version:/i) + 9).split(/,/); + status.machine.firmware.date = new Date(data.substr(data.search(/Build date:/i) + 12).split(/,/)).toDateString(); + // Start interval for status queries + // statusLoop = setInterval(function() { + // if (status.comms.connectionStatus > 0) { + // addQRealtime("?"); + // } + // }, 200); + var output = { + 'command': "FIRMWARE ERROR", + 'response': "Detected an unsupported version: Smoothieware " + status.machine.firmware.version + ". This software no longer support Smoothieware. \nLuckilly there is an alternative firmware you can install on your controller to make it work with this software. Check out Grbl-LPC at https://github.com/cprezzi/grbl-LPC - Grbl-LPC is a Grbl port for controllers using the NXP LPC176x chips, for example Smoothieboards", + 'type': 'error' + } + io.sockets.emit('data', output); + stopPort(); + } // end of machine identification + + // Machine Feedback: Position + if (data.indexOf("<") === 0) { + // debug_log(' Got statusReport (Grbl & Smoothieware)') + // statusfeedback func + parseFeedback(data) + if (command == "?") { + var output = { + 'command': command, + 'response': data, + 'type': 'info' + } + // debug_log(output.response) + io.sockets.emit('data', output); + } + // debug_log(data) + } else if (data.indexOf("ok") === 0) { // Got an OK so we are clear to send + io.sockets.emit('ok', command); // added per #325 + // debug_log("OK FOUND") + if (status.machine.firmware.type === "grbl") { + // debug_log('got OK from ' + command) + command = sentBuffer.shift(); + } + if (command == "$CD") { + io.sockets.emit('fluidncConfig', fluidncConfig); + } + status.comms.blocked = false; + send1Q(); + } else if (data.indexOf('ALARM') === 0) { //} || data.indexOf('HALTED') === 0) { + debug_log("ALARM: " + data) + status.comms.connectionStatus = 5; + switch (status.machine.firmware.type) { + case 'grbl': + // sentBuffer.shift(); + var alarmCode = parseInt(data.split(':')[1]); + debug_log('ALARM: ' + alarmCode + ' - ' + grblStrings.alarms(alarmCode)); + status.comms.alarm = alarmCode + ' - ' + grblStrings.alarms(alarmCode) + if (alarmCode != 5) { + io.sockets.emit("toastErrorAlarm", 'ALARM: ' + alarmCode + ' - ' + grblStrings.alarms(alarmCode) + " [ " + command + " ]") + } + var output = { + 'command': '', + 'response': 'ALARM: ' + alarmCode + ' - ' + grblStrings.alarms(alarmCode) + " [ " + command + " ]", + 'type': 'error' + } + io.sockets.emit('data', output); + break; + } + status.comms.connectionStatus = 5; + } else if (data.indexOf('WARNING: After HALT you should HOME as position is currently unknown') != -1) { //} || data.indexOf('HALTED') === 0) { + status.comms.connectionStatus = 2; + } else if (data.indexOf('Emergency Stop Requested') != -1) { //} || data.indexOf('HALTED') === 0) { + debug_log("Emergency Stop Requested") + status.comms.connectionStatus = 5; + } else if (data.indexOf('wait') === 0) { // Got wait from Repetier -> ignore + // do nothing + } else if (data.indexOf('error') === 0) { // Error received -> stay blocked stops queue + switch (status.machine.firmware.type) { + case 'grbl': + // sentBuffer.shift(); + var errorCode = parseInt(data.split(':')[1]); + + var lastAlarm = ""; + if (errorCode == 9 && status.comms.connectionStatus == 5 && status.comms.alarm.length > 0) { + lastAlarm = "