diff --git a/README.md b/README.md
index eb99754..d4b1183 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,11 @@
## Overview
-Upgrade the Malyan 200 or the Monoprice Select Mini's (V1 & V2) Web UI and enable faster Wi-Fi file uploads automatically.
+Upgrade the Malyan M200 or the Monoprice Select Mini's V1 Web UI and enable faster Wi-Fi file uploads automatically. For V2, download the [V2 branch](https://github.com/nokemono42/MP-Select-Mini-Web/tree/v2).
-Built using Bootstrap so the UI is mobile-friendly and tablet-friendly.
+Requires UI Controller firmware version 42 to enable the custom Web UI.
+
+This Web UI is built using Bootstrap so its mobile-friendly and tablet-friendly. Multiple browser connections are supported. The GCode commands are sent via Web Sockets so all browser windows will display the printer responses.
![Image of the WebUI](https://raw.githubusercontent.com/nokemono42/MP-Select-Mini-Web/master/screenshot.png)
@@ -23,8 +25,19 @@ Built using Bootstrap so the UI is mobile-friendly and tablet-friendly.
## Enable Faster Wi-Fi File Uploads
-By default the upgraded Web UI will send `M563 S5` on each refresh to ensure faster Wi-Fi file uploads is enabled. Note: Since the V2 uses `M563 S5` this will be the initial start up code, instead of `M563 S6`.
+By default the upgraded Web UI will send `M563 S6` on each refresh to ensure faster Wi-Fi file uploads is enabled. This setting doesn't persist after the printer had been powered off.
+
+Note: Since S6 is currently broken due to V2 firmware bug the V2 uses `M563 S5`. See [V2 branch](https://github.com/nokemono42/MP-Select-Mini-Web/tree/v2).
+
+ S values can be 2 - 6. Transfers happen over telnet which blocks the sending of any other GCode commands and limits how fast the files can be transfered. The sweet spot seems to be less then 12 MB of GCode. Files larger then that take over 2 minutes to transfer.
+| M563 S# | Avg Transfer Speed | Supported On |
+| ------- | -----------------: | ---------------- |
+| S2 | 39 Kbps | Firmware Default |
+| S3 | 63 Kbps | All |
+| S4 | 90 Kbps | All |
+| S5 | 102 Kbps | Only V2 / Delta |
+| S6 | 112 Kbps | Only V1 |
## Offline Usage
@@ -56,13 +69,11 @@ Mario Anthony Galliano (Facebook Group posting with upgrade/downgrade instructio
## Upcoming Improvements
* Test on MPSM V2
+* Change multiplier
* Show time lasped / time remaining
* Show filename that is printing
-* Change multiplier
* Rename cache.gc file with M566 after upload
-* Print done / presentation button. (Gantry away put all the way forward.)
-* Query SD card for list of files
-* Delete file from SD card
-* Print file from SD card
-* Rename file from SD card
-* Refresh SD card
+* Query SD card for list of files M20
+* Delete file from SD card M30
+* Print file from SD card M24
+* Pause print M25
diff --git a/mpsm_v1.jpg b/mpsm_v1.jpg
new file mode 100644
index 0000000..ea88f94
Binary files /dev/null and b/mpsm_v1.jpg differ
diff --git a/mpsm_v1_255.jpg b/mpsm_v1_255.jpg
new file mode 100644
index 0000000..1638518
Binary files /dev/null and b/mpsm_v1_255.jpg differ
diff --git a/source/swfobject.js b/source/swfobject.js
new file mode 100755
index 0000000..8eafe9d
--- /dev/null
+++ b/source/swfobject.js
@@ -0,0 +1,4 @@
+/* SWFObject v2.2
+ is released under the MIT License
+*/
+var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab
+// License: New BSD License
+// Reference: http://dev.w3.org/html5/websockets/
+// Reference: http://tools.ietf.org/html/rfc6455
+
+(function() {
+
+ if (window.WEB_SOCKET_FORCE_FLASH) {
+ // Keeps going.
+ } else if (window.WebSocket) {
+ return;
+ } else if (window.MozWebSocket) {
+ // Firefox.
+ window.WebSocket = MozWebSocket;
+ return;
+ }
+
+ var logger;
+ if (window.WEB_SOCKET_LOGGER) {
+ logger = WEB_SOCKET_LOGGER;
+ } else if (window.console && window.console.log && window.console.error) {
+ // In some environment, console is defined but console.log or console.error is missing.
+ logger = window.console;
+ } else {
+ logger = {log: function(){ }, error: function(){ }};
+ }
+
+ // swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
+ if (swfobject.getFlashPlayerVersion().major < 10) {
+ logger.error("Flash Player >= 10.0.0 is required.");
+ return;
+ }
+ if (location.protocol == "file:") {
+ logger.error(
+ "WARNING: web-socket-js doesn't work in file:///... URL " +
+ "unless you set Flash Security Settings properly. " +
+ "Open the page via Web server i.e. http://...");
+ }
+
+ /**
+ * Our own implementation of WebSocket class using Flash.
+ * @param {string} url
+ * @param {array or string} protocols
+ * @param {string} proxyHost
+ * @param {int} proxyPort
+ * @param {string} headers
+ */
+ window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
+ var self = this;
+ self.__id = WebSocket.__nextId++;
+ WebSocket.__instances[self.__id] = self;
+ self.readyState = WebSocket.CONNECTING;
+ self.bufferedAmount = 0;
+ self.__events = {};
+ if (!protocols) {
+ protocols = [];
+ } else if (typeof protocols == "string") {
+ protocols = [protocols];
+ }
+ // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
+ // Otherwise, when onopen fires immediately, onopen is called before it is set.
+ self.__createTask = setTimeout(function() {
+ WebSocket.__addTask(function() {
+ self.__createTask = null;
+ WebSocket.__flash.create(
+ self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
+ });
+ }, 0);
+ };
+
+ /**
+ * Send data to the web socket.
+ * @param {string} data The data to send to the socket.
+ * @return {boolean} True for success, false for failure.
+ */
+ WebSocket.prototype.send = function(data) {
+ if (this.readyState == WebSocket.CONNECTING) {
+ throw "INVALID_STATE_ERR: Web Socket connection has not been established";
+ }
+ // We use encodeURIComponent() here, because FABridge doesn't work if
+ // the argument includes some characters. We don't use escape() here
+ // because of this:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
+ // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
+ // preserve all Unicode characters either e.g. "\uffff" in Firefox.
+ // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
+ // additional testing.
+ var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
+ if (result < 0) { // success
+ return true;
+ } else {
+ this.bufferedAmount += result;
+ return false;
+ }
+ };
+
+ /**
+ * Close this web socket gracefully.
+ */
+ WebSocket.prototype.close = function() {
+ if (this.__createTask) {
+ clearTimeout(this.__createTask);
+ this.__createTask = null;
+ this.readyState = WebSocket.CLOSED;
+ return;
+ }
+ if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
+ return;
+ }
+ this.readyState = WebSocket.CLOSING;
+ WebSocket.__flash.close(this.__id);
+ };
+
+ /**
+ * Implementation of {@link DOM 2 EventTarget Interface}
+ *
+ * @param {string} type
+ * @param {function} listener
+ * @param {boolean} useCapture
+ * @return void
+ */
+ WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
+ if (!(type in this.__events)) {
+ this.__events[type] = [];
+ }
+ this.__events[type].push(listener);
+ };
+
+ /**
+ * Implementation of {@link DOM 2 EventTarget Interface}
+ *
+ * @param {string} type
+ * @param {function} listener
+ * @param {boolean} useCapture
+ * @return void
+ */
+ WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
+ if (!(type in this.__events)) return;
+ var events = this.__events[type];
+ for (var i = events.length - 1; i >= 0; --i) {
+ if (events[i] === listener) {
+ events.splice(i, 1);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Implementation of {@link DOM 2 EventTarget Interface}
+ *
+ * @param {Event} event
+ * @return void
+ */
+ WebSocket.prototype.dispatchEvent = function(event) {
+ var events = this.__events[event.type] || [];
+ for (var i = 0; i < events.length; ++i) {
+ events[i](event);
+ }
+ var handler = this["on" + event.type];
+ if (handler) handler.apply(this, [event]);
+ };
+
+ /**
+ * Handles an event from Flash.
+ * @param {Object} flashEvent
+ */
+ WebSocket.prototype.__handleEvent = function(flashEvent) {
+
+ if ("readyState" in flashEvent) {
+ this.readyState = flashEvent.readyState;
+ }
+ if ("protocol" in flashEvent) {
+ this.protocol = flashEvent.protocol;
+ }
+
+ var jsEvent;
+ if (flashEvent.type == "open" || flashEvent.type == "error") {
+ jsEvent = this.__createSimpleEvent(flashEvent.type);
+ } else if (flashEvent.type == "close") {
+ jsEvent = this.__createSimpleEvent("close");
+ jsEvent.wasClean = flashEvent.wasClean ? true : false;
+ jsEvent.code = flashEvent.code;
+ jsEvent.reason = flashEvent.reason;
+ } else if (flashEvent.type == "message") {
+ var data = decodeURIComponent(flashEvent.message);
+ jsEvent = this.__createMessageEvent("message", data);
+ } else {
+ throw "unknown event type: " + flashEvent.type;
+ }
+
+ this.dispatchEvent(jsEvent);
+
+ };
+
+ WebSocket.prototype.__createSimpleEvent = function(type) {
+ if (document.createEvent && window.Event) {
+ var event = document.createEvent("Event");
+ event.initEvent(type, false, false);
+ return event;
+ } else {
+ return {type: type, bubbles: false, cancelable: false};
+ }
+ };
+
+ WebSocket.prototype.__createMessageEvent = function(type, data) {
+ if (window.MessageEvent && typeof(MessageEvent) == "function" && !window.opera) {
+ return new MessageEvent("message", {
+ "view": window,
+ "bubbles": false,
+ "cancelable": false,
+ "data": data
+ });
+ } else if (document.createEvent && window.MessageEvent && !window.opera) {
+ var event = document.createEvent("MessageEvent");
+ event.initMessageEvent("message", false, false, data, null, null, window, null);
+ return event;
+ } else {
+ // Old IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
+ return {type: type, data: data, bubbles: false, cancelable: false};
+ }
+ };
+
+ /**
+ * Define the WebSocket readyState enumeration.
+ */
+ WebSocket.CONNECTING = 0;
+ WebSocket.OPEN = 1;
+ WebSocket.CLOSING = 2;
+ WebSocket.CLOSED = 3;
+
+ // Field to check implementation of WebSocket.
+ WebSocket.__isFlashImplementation = true;
+ WebSocket.__initialized = false;
+ WebSocket.__flash = null;
+ WebSocket.__instances = {};
+ WebSocket.__tasks = [];
+ WebSocket.__nextId = 0;
+
+ /**
+ * Load a new flash security policy file.
+ * @param {string} url
+ */
+ WebSocket.loadFlashPolicyFile = function(url){
+ WebSocket.__addTask(function() {
+ WebSocket.__flash.loadManualPolicyFile(url);
+ });
+ };
+
+ /**
+ * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
+ */
+ WebSocket.__initialize = function() {
+
+ if (WebSocket.__initialized) return;
+ WebSocket.__initialized = true;
+
+ if (WebSocket.__swfLocation) {
+ // For backword compatibility.
+ window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
+ }
+ if (!window.WEB_SOCKET_SWF_LOCATION) {
+ logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
+ return;
+ }
+ if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
+ !WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
+ WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
+ var swfHost = RegExp.$1;
+ if (location.host != swfHost) {
+ logger.error(
+ "[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
+ "('" + location.host + "' != '" + swfHost + "'). " +
+ "See also 'How to host HTML file and SWF file in different domains' section " +
+ "in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
+ "by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
+ }
+ }
+ var container = document.createElement("div");
+ container.id = "webSocketContainer";
+ // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
+ // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
+ // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
+ // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
+ // the best we can do as far as we know now.
+ container.style.position = "absolute";
+ if (WebSocket.__isFlashLite()) {
+ container.style.left = "0px";
+ container.style.top = "0px";
+ } else {
+ container.style.left = "-100px";
+ container.style.top = "-100px";
+ }
+ var holder = document.createElement("div");
+ holder.id = "webSocketFlash";
+ container.appendChild(holder);
+ document.body.appendChild(container);
+ // See this article for hasPriority:
+ // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
+ swfobject.embedSWF(
+ WEB_SOCKET_SWF_LOCATION,
+ "webSocketFlash",
+ "1" /* width */,
+ "1" /* height */,
+ "10.0.0" /* SWF version */,
+ null,
+ null,
+ {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
+ null,
+ function(e) {
+ if (!e.success) {
+ logger.error("[WebSocket] swfobject.embedSWF failed");
+ }
+ }
+ );
+
+ };
+
+ /**
+ * Called by Flash to notify JS that it's fully loaded and ready
+ * for communication.
+ */
+ WebSocket.__onFlashInitialized = function() {
+ // We need to set a timeout here to avoid round-trip calls
+ // to flash during the initialization process.
+ setTimeout(function() {
+ WebSocket.__flash = document.getElementById("webSocketFlash");
+ WebSocket.__flash.setCallerUrl(location.href);
+ WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
+ for (var i = 0; i < WebSocket.__tasks.length; ++i) {
+ WebSocket.__tasks[i]();
+ }
+ WebSocket.__tasks = [];
+ }, 0);
+ };
+
+ /**
+ * Called by Flash to notify WebSockets events are fired.
+ */
+ WebSocket.__onFlashEvent = function() {
+ setTimeout(function() {
+ try {
+ // Gets events using receiveEvents() instead of getting it from event object
+ // of Flash event. This is to make sure to keep message order.
+ // It seems sometimes Flash events don't arrive in the same order as they are sent.
+ var events = WebSocket.__flash.receiveEvents();
+ for (var i = 0; i < events.length; ++i) {
+ WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
+ }
+ } catch (e) {
+ logger.error(e);
+ }
+ }, 0);
+ return true;
+ };
+
+ // Called by Flash.
+ WebSocket.__log = function(message) {
+ logger.log(decodeURIComponent(message));
+ };
+
+ // Called by Flash.
+ WebSocket.__error = function(message) {
+ logger.error(decodeURIComponent(message));
+ };
+
+ WebSocket.__addTask = function(task) {
+ if (WebSocket.__flash) {
+ task();
+ } else {
+ WebSocket.__tasks.push(task);
+ }
+ };
+
+ /**
+ * Test if the browser is running flash lite.
+ * @return {boolean} True if flash lite is running, false otherwise.
+ */
+ WebSocket.__isFlashLite = function() {
+ if (!window.navigator || !window.navigator.mimeTypes) {
+ return false;
+ }
+ var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
+ if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
+ return false;
+ }
+ return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
+ };
+
+ if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
+ // NOTE:
+ // This fires immediately if web_socket.js is dynamically loaded after
+ // the document is loaded.
+ swfobject.addDomLoadEvent(function() {
+ WebSocket.__initialize();
+ });
+ }
+
+})();
diff --git a/source/web_socket.min.js b/source/web_socket.min.js
new file mode 100644
index 0000000..a98235e
--- /dev/null
+++ b/source/web_socket.min.js
@@ -0,0 +1 @@
+(function(){if(window.WEB_SOCKET_FORCE_FLASH);else{if(window.WebSocket)return;if(window.MozWebSocket)return void(window.WebSocket=MozWebSocket)}var e;return e=window.WEB_SOCKET_LOGGER?WEB_SOCKET_LOGGER:window.console&&window.console.log&&window.console.error?window.console:{log:function(){},error:function(){}},swfobject.getFlashPlayerVersion().major<10?void e.error("Flash Player >= 10.0.0 is required."):("file:"==location.protocol&&e.error("WARNING: web-socket-js doesn't work in file:///... URL unless you set Flash Security Settings properly. Open the page via Web server i.e. http://..."),window.WebSocket=function(e,t,o,n,i){var s=this;s.__id=WebSocket.__nextId++,WebSocket.__instances[s.__id]=s,s.readyState=WebSocket.CONNECTING,s.bufferedAmount=0,s.__events={},t?"string"==typeof t&&(t=[t]):t=[],s.__createTask=setTimeout(function(){WebSocket.__addTask(function(){s.__createTask=null,WebSocket.__flash.create(s.__id,e,t,o||null,n||0,i||null)})},0)},WebSocket.prototype.send=function(e){if(this.readyState==WebSocket.CONNECTING)throw"INVALID_STATE_ERR: Web Socket connection has not been established";var t=WebSocket.__flash.send(this.__id,encodeURIComponent(e));return t<0||(this.bufferedAmount+=t,!1)},WebSocket.prototype.close=function(){return this.__createTask?(clearTimeout(this.__createTask),this.__createTask=null,void(this.readyState=WebSocket.CLOSED)):void(this.readyState!=WebSocket.CLOSED&&this.readyState!=WebSocket.CLOSING&&(this.readyState=WebSocket.CLOSING,WebSocket.__flash.close(this.__id)))},WebSocket.prototype.addEventListener=function(e,t,o){e in this.__events||(this.__events[e]=[]),this.__events[e].push(t)},WebSocket.prototype.removeEventListener=function(e,t,o){if(e in this.__events)for(var n=this.__events[e],i=n.length-1;i>=0;--i)if(n[i]===t){n.splice(i,1);break}},WebSocket.prototype.dispatchEvent=function(e){for(var t=this.__events[e.type]||[],o=0;o' + data + '
');
- //scrollConsole();
-
- $("#rde").text(data.match( /\d+/g )[0]);
- $("#rdp").text(data.match( /\d+/g )[2]);
-
- delaySyncTemperatures(data.match( /\d+/g )[1], data.match( /\d+/g )[3]);
-
- var c = data.charAt(data.length - 1);
-
- if (c == 'I') {
- $("#stat").text('Idle');
- $("#pgs").css('width', '0%');
- $(".movement button").removeClass('btn-disable');
- $("#gCodeSend").removeClass('btn-disable');
- } else if (c == 'P') {
- $("#stat").text('Printing');
- $("#pgs").css('width', data.match(/\d+/g )[4] + '%');
- $("#pgs").html(data.match(/\d+/g )[4] + '% Complete');
- $(".movement button").addClass('btn-disable');
- $("#gCodeSend").addClass('btn-disable');
- } else {
- $("#stat").text('N/A');
- }
- });
- }, 3500);
- /* */
+$(document).ready(function() {
+ printerStatus();
+ startup();
+
+ setInterval(function() {
+ printerStatus();
+ }, 2000);
$(".movement .home").click(function() {
axis = $(this).attr("data-axis");
@@ -93,13 +63,11 @@ $(document).ready( function() {
});
$("#wre").change(function() {
- var value = pad($("#wre").val(), 3);
- sendCmd( '{C:T0' + value + '}', 'Set extruder preheat to ' + $("#wre").val() + '°C', 'cmd' );
+ delaySendTemp($("#wre").val(), 'extruder');
});
$("#sete").click(function() {
- var value = pad($("#wre").val(), 3);
- sendCmd('{C:T0' + value + '}', 'Set extruder preheat to ' + $("#wre").val() + '°C', 'cmd');
+ delaySendTemp($("#wre").val(), 'extruder');
});
$("#clre").click(function() {
@@ -107,13 +75,11 @@ $(document).ready( function() {
});
$("#wrp").change(function() {
- value = pad($("#wrp").val(), 3);
- sendCmd('{C:P' + value + '}', 'Set platform preheat to ' + $("#wrp").val() + '°C', 'cmd');
+ delaySendTemp($("#wrp").val(), 'platform');
});
$("#setp").click(function() {
- value = pad($("#wrp").val(), 3);
- sendCmd('{C:P' + value + '}', 'Set platform preheat to ' + $("#wrp").val() + '°C', 'cmd');
+ delaySendTemp($("#wrp").val(), 'platform');
});
$("#clrp").click(function() {
@@ -128,7 +94,7 @@ $(document).ready( function() {
}
});
- $("#fanspeed").on('slide', function(slideEvt) {
+ $("#fanspeed").on('slide', function(slideEvt) {
delaySendSpeed(slideEvt.value);
});
@@ -153,34 +119,76 @@ function scrollConsole() {
$cont[0].scrollTop = $cont[0].scrollHeight;
}
-function sendCmd(cmd, comment, type) {
+function feedback(output) {
+ msg = output.replace(/N0 P15 B15/g, '');
+ msg = msg.replace(/N0 P14 B15/g, '');
+ msg = msg.replace(/echo:/g, '');
+ $("#gCodeLog").append('' + msg + '
');
+ scrollConsole();
+}
+
+function sendCmd(code, comment, type) {
if (type === undefined) { type = "code"; }
- clearTimeout(timers);
- timers = setTimeout(function() {
- $("#gCodeLog").append('' + cmd + ' ; ' + comment +'
');
+
+ $("#gCodeLog").append('' + code + ' ; ' + comment + '
');
- $.ajax({ url: 'set?' + type + '=' + cmd, cache: false }).done(function(data) {
- $("#gCodeLog").append('' + data + '
');
- scrollConsole();
+ if (type == 'cmd') {
+ $.ajax({ url: 'set?' + type + '=' + code, cache: false }).done(function(data) {
+ feedback(data);
});
+ } else {
+ ws.send(code);
+ }
- scrollConsole();
- }, 300);
+ scrollConsole();
}
-function feedback(output) {
- $("#gCodeLog").append('' + output + '
');
- scrollConsole();
+function initWebSocket() {
+ url = window.location.hostname;
+
+ try {
+ ws = new WebSocket('ws://' + url + ':81');
+ ws.onopen = function() {
+ feedback('Connected');
+ };
+ ws.onmessage = function(a) {
+ feedback(a.data);
+ };
+ ws.onclose = function() {
+ feedback('Disconnected');
+ }
+ } catch (a) {
+ feedback('Web Socket Error');
+ }
}
-String.prototype.contains = function( it ) {
- return this.indexOf( it ) != -1;
+function msToTime(duration) {
+ var milliseconds = parseInt((duration%1000)/100),
+ seconds = parseInt((duration/1000)%60),
+ minutes = parseInt((duration/(1000*60))%60),
+ hours = parseInt((duration/(1000*60*60))%24);
+
+ hours = (hours < 10) ? "0" + hours : hours;
+ minutes = (minutes < 10) ? "0" + minutes : minutes;
+ seconds = (seconds < 10) ? "0" + seconds : seconds;
+
+ return hours + ":" + minutes + ":" + seconds + "." + milliseconds;
+}
+
+String.prototype.contains = function(it) {
+ return this.indexOf(it) != -1;
};
-Dropzone.options.mydz = { dictDefaultMessage: "Upload G-code Here",
+Dropzone.options.mydz = {
accept: function(file, done) {
if (file.name.contains('.g')) {
+ //window.startTimer = new Date();
+
done();
+ $(".print-actions button").addClass('btn-disable');
+ $(".movement button").addClass('btn-disable');
+ $("#gCodeSend").addClass('btn-disable');
+ $(".temperature button").addClass('btn-disable');
} else {
done('Not a valid G-code file.');
}
@@ -195,6 +203,19 @@ Dropzone.options.mydz = { dictDefaultMessage: "Upload G-code Here",
this.removeFile(this.files[0]);
}
});
+
+ this.on('complete', function(file) {
+ //File upload duration
+ //endTimer = new Date();
+ //duration = endTimer - window.startTimer;
+ //alert(msToTime(duration));
+
+ $(".print-actions button").removeClass('btn-disable');
+ $(".movement button").removeClass('btn-disable');
+ $("#gCodeSend").removeClass('btn-disable');
+ $(".temperature button").removeClass('btn-disable');
+ sendCmd('M566 ' + file.name, '');
+ });
}
};
@@ -208,15 +229,70 @@ function cancel_p() {
sendCmd('{P:X}', 'Cancel print', 'cmd');
}
+function printerStatus() {
+ $.get("inquiry", function(data, status) {
+ //console.log(data);
+ //$("#gCodeLog").append('' + data + '
');
+ //scrollConsole();
+
+ $("#rde").text(data.match( /\d+/g )[0]);
+ $("#rdp").text(data.match( /\d+/g )[2]);
+
+ delaySyncTemperatures(data.match( /\d+/g )[1], data.match( /\d+/g )[3]);
+
+ var c = data.charAt(data.length - 1);
+
+ if (c == 'I') {
+ $("#stat").text('Idle');
+ $("#pgs").css('width', '0%');
+ $(".movement button").removeClass('btn-disable');
+ $("#gCodeSend").removeClass('btn-disable');
+ } else if (c == 'P') {
+ $("#stat").text('Printing');
+ $("#pgs").css('width', data.match(/\d+/g )[4] + '%');
+ $("#pgs").html(data.match(/\d+/g )[4] + '% Complete');
+ $(".movement button").addClass('btn-disable');
+ $("#gCodeSend").addClass('btn-disable');
+ } else {
+ $("#stat").text('N/A');
+ }
+ });
+}
+
+function startup() {
+ initWebSocket();
+
+ if ($("#stat").text() != 'Printing') {
+ setTimeout(function() {
+ sendCmd('M563 S4', 'Enable faster Wi-Fi file uploads');
+ sendCmd('G91', 'Set to Relative Positioning');
+ }, 1750);
+ } else {
+ alert('Printing');
+ }
+}
+
+function delaySendTemp(value, device) {
+ clearTimeout(timers);
+ timers = setTimeout(function() {
+ compValue = pad(value, 3);
+
+ if (device == 'extruder') {
+ sendCmd('{C:T0' + compValue + '}', 'Set extruder preheat to ' + value + '°C', 'cmd');
+ }
+
+ if (device == 'platform') {
+ sendCmd('{C:P' + compValue + '}', 'Set platform preheat to ' + value + '°C', 'cmd');
+ }
+ }, 250);
+}
+
function delaySendSpeed(value) {
clearTimeout(timers);
timers = setTimeout(function() {
actualSpeed = Math.floor(255 * (value/100));
sendCmd('M106 S' + actualSpeed, 'Set fan speed to ' + value + '%');
- $.ajax({ url: "set?code=M106 S" + value, cache: false }).done(
- function(data) { feedback(data); }
- );
- }, 300);
+ }, 250);
}
function delaySyncTemperatures(extruder, platform) {
@@ -225,4 +301,8 @@ function delaySyncTemperatures(extruder, platform) {
if (!$('#wre').is(":focus")) { $("#wre").val(extruder); }
if (!$('#wrp').is(":focus")) { $("#wrp").val(platform); }
}, 3000);
+}
+
+function refreshSD() {
+ sendCmd('M563 S3', 'Enable faster Wi-Fi file uploads');
}
\ No newline at end of file
diff --git a/source/webui.min.js b/source/webui.min.js
index 9b0a4d2..7444f14 100644
--- a/source/webui.min.js
+++ b/source/webui.min.js
@@ -1 +1 @@
-function pad(e,t){return s="000"+e,s.substr(s.length-t)}function scrollConsole(){$cont=$("#console"),$cont[0].scrollTop=$cont[0].scrollHeight}function sendCmd(e,t,n){void 0===n&&(n="code"),clearTimeout(timers),timers=setTimeout(function(){$("#gCodeLog").append(''+e+' ; '+t+"
"),$.ajax({url:"set?"+n+"="+e,cache:!1}).done(function(e){$("#gCodeLog").append(''+e+"
"),scrollConsole()}),scrollConsole()},300)}function feedback(e){$("#gCodeLog").append(''+e+"
"),scrollConsole()}function start_p(){$("#stat").text("Printing"),sendCmd("M565","Start printing cache.gc")}function cancel_p(){$("#stat").text("Canceling"),sendCmd("{P:X}","Cancel print","cmd")}function delaySendSpeed(e){clearTimeout(timers),timers=setTimeout(function(){actualSpeed=Math.floor(255*(e/100)),sendCmd("M106 S"+actualSpeed,"Set fan speed to "+e+"%"),$.ajax({url:"set?code=M106 S"+e,cache:!1}).done(function(e){feedback(e)})},300)}function delaySyncTemperatures(e,t){clearTimeout(timers),timers=setTimeout(function(){$("#wre").is(":focus")||$("#wre").val(e),$("#wrp").is(":focus")||$("#wrp").val(t)},3e3)}$(document).ready(function(){sendCmd("M563 S5","Enable faster Wi-Fi file uploads"),$.ajax({url:"set?code=G91",cache:!1}).done(),setInterval(function(){$.get("inquiry",function(e,t){console.log(e),$("#rde").text(e.match(/\d+/g)[0]),$("#rdp").text(e.match(/\d+/g)[2]),delaySyncTemperatures(e.match(/\d+/g)[1],e.match(/\d+/g)[3]);var n=e.charAt(e.length-1);"I"==n?($("#stat").text("Idle"),$("#pgs").css("width","0%"),$(".movement button").removeClass("btn-disable"),$("#gCodeSend").removeClass("btn-disable")):"P"==n?($("#stat").text("Printing"),$("#pgs").css("width",e.match(/\d+/g)[4]+"%"),$("#pgs").html(e.match(/\d+/g)[4]+"% Complete"),$(".movement button").addClass("btn-disable"),$("#gCodeSend").addClass("btn-disable")):$("#stat").text("N/A")})},3500),$(".movement .home").click(function(){axis=$(this).attr("data-axis"),"all"==axis?(code="G28 X0 Y0 Z0",comment="all axes"):(code="G28 "+axis,comment=axis+" axis"),sendCmd(code,"Home "+comment)}),$(".movement .direction button").click(function(){return command="G1 ",movement=$(this).attr("data-movement"),rate=$(".movement .rate button.active").attr("data-rate"),axis=$(this).attr("data-axis"),comment="Move "+axis,"up"!=movement&&"left"!=movement||(rate*=-1),"Z"==axis&&"down"==movement&&(comment="Raise Z "),"Z"==axis&&"up"==movement&&(comment="Lower Z "),"E"==axis&&"plus"==movement&&(comment="Extrude "),"E"==axis&&"minus"==movement?(sendCmd(command+axis+"-"+rate,"Retract "+rate+"mm"),!1):"disable"==movement?(sendCmd("M18","Disable motor lock"),!1):void sendCmd(command+axis+rate,comment+" "+rate+"mm")}),$(".movement .rate button").click(function(){rate=$(this).attr("data-rate"),$(".movement .rate button").removeClass("active"),$(this).addClass("active")}),$("#gCodeSend").click(function(){gCode2Send=$("#gcode").val(),""!=gCode2Send&&(sendCmd(gCode2Send,""),$("#gcode").val(""))}),$("#wre").change(function(){var e=pad($("#wre").val(),3);sendCmd("{C:T0"+e+"}","Set extruder preheat to "+$("#wre").val()+"°C","cmd")}),$("#sete").click(function(){var e=pad($("#wre").val(),3);sendCmd("{C:T0"+e+"}","Set extruder preheat to "+$("#wre").val()+"°C","cmd")}),$("#clre").click(function(){sendCmd("{C:T0000}","Turn off extruder preheat","cmd")}),$("#wrp").change(function(){value=pad($("#wrp").val(),3),sendCmd("{C:P"+value+"}","Set platform preheat to "+$("#wrp").val()+"°C","cmd")}),$("#setp").click(function(){value=pad($("#wrp").val(),3),sendCmd("{C:P"+value+"}","Set platform preheat to "+$("#wrp").val()+"°C","cmd")}),$("#clrp").click(function(){sendCmd("{C:P000}","Turn off platform preheat","cmd")}),$("#fanspeed").slider({min:30,max:100,value:50,reversed:!0,orientation:"vertical",formatter:function(e){return e+"%"}}),$("#fanspeed").on("slide",function(e){delaySendSpeed(e.value)}),$("#clrfan").click(function(){sendCmd("M106 S0","Turn off fan")}),$("form").submit(function(){return!1})});var timers={};String.prototype.contains=function(e){return this.indexOf(e)!=-1},Dropzone.options.mydz={dictDefaultMessage:"Upload G-code Here",accept:function(e,t){e.name.contains(".g")?t():t("Not a valid G-code file.")},init:function(){this.on("error",function(e,t){var n=t.errorMessage;$(e.previewElement).find(".dz-error-message").text(n)}),this.on("addedfile",function(){null!=this.files[1]&&this.removeFile(this.files[0])})}};
\ No newline at end of file
+function pad(e,t){return s="000"+e,s.substr(s.length-t)}function scrollConsole(){$cont=$("#console"),$cont[0].scrollTop=$cont[0].scrollHeight}function feedback(e){msg=e.replace(/N0 P15 B15/g,""),msg=msg.replace(/N0 P14 B15/g,""),msg=msg.replace(/echo:/g,""),$("#gCodeLog").append(''+msg+"
"),scrollConsole()}function sendCmd(e,t,n){void 0===n&&(n="code"),$("#gCodeLog").append(''+e+' ; '+t+"
"),"cmd"==n?$.ajax({url:"set?"+n+"="+e,cache:!1}).done(function(e){feedback(e)}):ws.send(e),scrollConsole()}function initWebSocket(){url=window.location.hostname;try{ws=new WebSocket("ws://"+url+":81"),ws.onopen=function(){feedback("Connected")},ws.onmessage=function(e){feedback(e.data)},ws.onclose=function(){feedback("Disconnected")}}catch(e){feedback("Web Socket Error")}}function msToTime(e){var t=parseInt(e%1e3/100),n=parseInt(e/1e3%60),a=parseInt(e/6e4%60),o=parseInt(e/36e5%24);return o=o<10?"0"+o:o,a=a<10?"0"+a:a,n=n<10?"0"+n:n,o+":"+a+":"+n+"."+t}function start_p(){$("#stat").text("Printing"),sendCmd("M565","Start printing cache.gc")}function cancel_p(){$("#stat").text("Canceling"),sendCmd("{P:X}","Cancel print","cmd")}function printerStatus(){$.get("inquiry",function(e,t){$("#rde").text(e.match(/\d+/g)[0]),$("#rdp").text(e.match(/\d+/g)[2]),delaySyncTemperatures(e.match(/\d+/g)[1],e.match(/\d+/g)[3]);var n=e.charAt(e.length-1);"I"==n?($("#stat").text("Idle"),$("#pgs").css("width","0%"),$(".movement button").removeClass("btn-disable"),$("#gCodeSend").removeClass("btn-disable")):"P"==n?($("#stat").text("Printing"),$("#pgs").css("width",e.match(/\d+/g)[4]+"%"),$("#pgs").html(e.match(/\d+/g)[4]+"% Complete"),$(".movement button").addClass("btn-disable"),$("#gCodeSend").addClass("btn-disable")):$("#stat").text("N/A")})}function startup(){initWebSocket(),"Printing"!=$("#stat").text()?setTimeout(function(){sendCmd("M563 S4","Enable faster Wi-Fi file uploads"),sendCmd("G91","Set to Relative Positioning")},1750):alert("Printing")}function delaySendTemp(e,t){clearTimeout(timers),timers=setTimeout(function(){compValue=pad(e,3),"extruder"==t&&sendCmd("{C:T0"+compValue+"}","Set extruder preheat to "+e+"°C","cmd"),"platform"==t&&sendCmd("{C:P"+compValue+"}","Set platform preheat to "+e+"°C","cmd")},250)}function delaySendSpeed(e){clearTimeout(timers),timers=setTimeout(function(){actualSpeed=Math.floor(255*(e/100)),sendCmd("M106 S"+actualSpeed,"Set fan speed to "+e+"%")},250)}function delaySyncTemperatures(e,t){clearTimeout(timers),timers=setTimeout(function(){$("#wre").is(":focus")||$("#wre").val(e),$("#wrp").is(":focus")||$("#wrp").val(t)},3e3)}function refreshSD(){sendCmd("M563 S3","Enable faster Wi-Fi file uploads")}$(document).ready(function(){printerStatus(),startup(),setInterval(function(){printerStatus()},2e3),$(".movement .home").click(function(){axis=$(this).attr("data-axis"),"all"==axis?(code="G28 X0 Y0 Z0",comment="all axes"):(code="G28 "+axis,comment=axis+" axis"),sendCmd(code,"Home "+comment)}),$(".movement .direction button").click(function(){return command="G1 ",movement=$(this).attr("data-movement"),rate=$(".movement .rate button.active").attr("data-rate"),axis=$(this).attr("data-axis"),comment="Move "+axis,"up"!=movement&&"left"!=movement||(rate*=-1),"Z"==axis&&"down"==movement&&(comment="Raise Z "),"Z"==axis&&"up"==movement&&(comment="Lower Z "),"E"==axis&&"plus"==movement&&(comment="Extrude "),"E"==axis&&"minus"==movement?(sendCmd(command+axis+"-"+rate,"Retract "+rate+"mm"),!1):"disable"==movement?(sendCmd("M18","Disable motor lock"),!1):void sendCmd(command+axis+rate,comment+" "+rate+"mm")}),$(".movement .rate button").click(function(){rate=$(this).attr("data-rate"),$(".movement .rate button").removeClass("active"),$(this).addClass("active")}),$("#gCodeSend").click(function(){gCode2Send=$("#gcode").val(),""!=gCode2Send&&(sendCmd(gCode2Send,""),$("#gcode").val(""))}),$("#wre").change(function(){delaySendTemp($("#wre").val(),"extruder")}),$("#sete").click(function(){delaySendTemp($("#wre").val(),"extruder")}),$("#clre").click(function(){sendCmd("{C:T0000}","Turn off extruder preheat","cmd")}),$("#wrp").change(function(){delaySendTemp($("#wrp").val(),"platform")}),$("#setp").click(function(){delaySendTemp($("#wrp").val(),"platform")}),$("#clrp").click(function(){sendCmd("{C:P000}","Turn off platform preheat","cmd")}),$("#fanspeed").slider({min:30,max:100,value:50,reversed:!0,orientation:"vertical",formatter:function(e){return e+"%"}}),$("#fanspeed").on("slide",function(e){delaySendSpeed(e.value)}),$("#clrfan").click(function(){sendCmd("M106 S0","Turn off fan")}),$("form").submit(function(){return!1})});var timers={};String.prototype.contains=function(e){return this.indexOf(e)!=-1},Dropzone.options.mydz={accept:function(e,t){e.name.contains(".g")?(t(),$(".print-actions button").addClass("btn-disable"),$(".movement button").addClass("btn-disable"),$("#gCodeSend").addClass("btn-disable"),$(".temperature button").addClass("btn-disable")):t("Not a valid G-code file.")},init:function(){this.on("error",function(e,t){var n=t.errorMessage;$(e.previewElement).find(".dz-error-message").text(n)}),this.on("addedfile",function(){null!=this.files[1]&&this.removeFile(this.files[0])}),this.on("complete",function(e){$(".print-actions button").removeClass("btn-disable"),$(".movement button").removeClass("btn-disable"),$("#gCodeSend").removeClass("btn-disable"),$(".temperature button").removeClass("btn-disable"),sendCmd("M566 "+e.name,"")})}};
\ No newline at end of file
diff --git a/webui.html b/webui.html
index 0e266d6..5540576 100755
--- a/webui.html
+++ b/webui.html
@@ -11,16 +11,16 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
@@ -34,7 +34,7 @@ Printer Status:
-
+
@@ -51,7 +51,8 @@
Printer Status:
-
@@ -211,16 +212,20 @@ N/A
-
-
-
-
+
+
+
+
-
+
+
+