diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9db24c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +icons.pcvd diff --git a/README.md b/README.md index 75f55df..9eff591 100644 --- a/README.md +++ b/README.md @@ -2,38 +2,57 @@ ## Overview -Upgrade the Malyan 200 or the Monoprice Select Mini's Web UI and enable faster Wi-Fi file transfers. Built using Bootstrap so the UI is mobile-friendly and tablet-friendly. +Upgrade the Malyan 200 or the Monoprice Select Mini's (V1) Web UI and enable faster Wi-Fi file uploads. -While you want to update the UI of the printer, you don't want the web server on the controller working too hard. This upgrade is designed to minimize the amount of data the web server has to serve. You browser will be doing move of the heavy lifting. +Built using Bootstrap so the UI is mobile-friendly and tablet-friendly. + +While you may want to update the UI of the printer, you don't want the web server on the controller working too hard. This upgrade is designed to minimize the amount of data the web server has to serve. You browser will be doing most of the heavy lifting. + +![Image of the WebUI](https://raw.githubusercontent.com/nokemono42/MP-Select-Mini-Web/web-ui-update/screenshot.png) ## Getting Started 1. Download and unzip `MP-Select-Mini-Web` from GitHub. 2. Point a web browser window to your printer's IP address. `http://IPAddressHere` 3. Now browse to the upgrade page. `http://IPAddressHere/up` -4. On the upgrade page, there are three options. We are only concerned with the last one, "Upload web." DO NOT CLICK THIS YET. -5. On the same line as "Upload web" click "Choose file" and select the "webui.html" file from the folder you unzipped earlier. -6. NOW you can click "Upload web." -6. If things worked, the response will display one word only. If you see "OK" you are good to go! +4. Click the third "Choose file" and select the "webui.html" file from the folder you unzipped earlier. +5. Click "Upload web." +6. If you see "OK" you are good to go! 7. It's recommended you power cycle your printer at this point. -8. Once your printer is back online, browse to `http://IPAddressHere`. You should now have the upgraded web UI with full manual control. +8. Once your printer is back online, browse to `http://IPAddressHere`. You should now have the upgraded Web UI with full manual control. -## Enable Faster Wifi File Transfers +## Enable Faster Wi-Fi File Uploads -The final step is to speed up your Wi-Fi uploading by pasting, `M563 S6 ;` in the "send gCode to printer" box and hitting send. You will need to send that gcode after every power cycle, though. +By default the upgraded Web UI will send `M563 S6` on each refresh to ensure faster Wi-Fi file uploads is enabled. ## Troubleshooting Did something break? Here's how you can undo the Web UI upgrade. -1. Turn off the printer. Wait about 10 seconds, and then turn it back on. -3. Once it's on go DIRECTLY to `http://IPAddressHere/up`. -4. Just click the "Upload web" button without choosing a file and it will restore the factory web UI. +1. Turn off the printer. Wait about 10 seconds and then turn it back on. +3. Once it's on, go DIRECTLY to `http://IPAddressHere/up`. +4. Just click the "Upload web" button without choosing a file and this will restore the factory web UI. ## Credits +Joey Cortez + Jason Jones (Original Code) -Matthew Upp (Middle Man / Beta Tester) +Matthew Upp (Middle Man) + +Mario Anthony Galliano (Facebook Group posting with upgrade/downgrade instructions.) + +## Upcoming Improvements -Mario Anthony Galliano (Facebook posting with upgrade/downgrade instructions.) +* Test on MPSM V2 +* 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 diff --git a/screenshot.png b/screenshot.png index faf60a4..29740dd 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/source/webui.css b/source/webui.css new file mode 100644 index 0000000..9089a01 --- /dev/null +++ b/source/webui.css @@ -0,0 +1,156 @@ +/* +Name: MP Select Mini Web CSS +URL: https://github.com/nokemono42/MP-Select-Mini-Web +*/ + +html { font-size: 16px; } + +body { + font-family: 'Roboto Condensed', sans-serif; + font-size: 1rem; + font-weight: 400; + line-height: 1.5rem; + color: #646C7F; +} + +h3, +h4 { + color: #0272b6; + font-weight: 300; +} + +h3 { + margin-bottom: 20px; + font-size: 1.5rem; +} + +h4 { margin-top: 0; } + +.btn { letter-spacing: 0.025rem; } +.btn-default { color: #646C7F; } + +.btn-disable { + pointer-events: none; + opacity: 0.4; +} + +footer { + padding-top: 15px; + padding-bottom: 10px; + font-size: 0.875rem; + text-align: right; +} + +@media screen and (max-width : 767px) { + footer.navbar-fixed-bottom { + position: relative; + text-align: center; + } +} + +.progress { height: 25px; } +.progress .progress-bar { padding-top: 3px; } + +.webcam { margin-bottom: 20px; } +.webcam img { + width: auto; + height: 255px; + } + +.print-actions button { + margin-bottom: 20px; + width: 100%; +} + +.dropzone { + min-height: 201px; + padding: 10px; + margin-bottom: 20px; + background-color: #fff; + border: 2px dashed #0272b6; + border-radius: 5px; + color: #00619a; +} + +.dropzone .dz-message { + top: 50%; + left: 0; + position: absolute; + width: 100%; + margin: 0; + font-weight: 500; + font-size: 1.25rem; + text-align: center; +} + +.dropzone .dz-preview { + width: 100%; + margin: 0; +} + +.dropzone .dz-preview .dz-image { display: none; } + +.dropzone .dz-preview .dz-details { + top: 50%; + color: inherit; + font-size: 0.875rem; +} + +.dropzone .dz-preview .dz-progress { + left: 0; + width: 100%; + margin-left: 0; +} + +.dropzone .dz-preview .dz-success-mark, +.dropzone .dz-preview .dz-error-mark { top: 40%; } + +.dropzone .dz-preview .dz-success-mark svg g path { fill: #2C9040; } +.dropzone .dz-preview .dz-error-mark svg g { fill: #FF2F3C; } + +.movement .row { margin-bottom: 14px; } +.movement .row.rate { margin-bottom: 20px; } +.movement .direction .text-center { line-height: 2rem; } + +.rate .center-block { width: 196px; } + +#console { + min-height: 201px; + max-height: 201px; + overflow: scroll; + padding: 0 8px; + font-family: monospace, monospace; + font-size: 0.875rem; +} + +#console p { margin: 0 0 4px; } + +#console .comment { color: #2C9040; } + +#gCodeInput { margin-bottom: 20px; } + +.temperature h3 { + margin-top: 41px; + margin-bottom: 40px; +} + +.temperature .target { + width: 100px; + margin-left: auto; + margin-right: auto; + margin-bottom: 30px; +} + +.slider { + display: block; + margin-left: auto; + margin-right: auto; + margin-bottom: 20px; +} + +.slider.slider-vertical { height: 141px; } + +#fanspeed .slider-selection { background: #0272b6; } + +#sete, +#setp { cursor: pointer; } diff --git a/source/webui.js b/source/webui.js new file mode 100644 index 0000000..1789a86 --- /dev/null +++ b/source/webui.js @@ -0,0 +1,220 @@ +/* +Name: MP Select Mini Web Javascript +URL: https://github.com/nokemono42/MP-Select-Mini-Web +*/ + +$(document).ready( function() { + sendCmd( 'M563 S6', 'Enable faster Wi-Fi file uploads' ); + // Set to Relative Positioning + $.ajax({ url: "set?code=G91", cache: false }).done(); + + /* */ + setInterval( function() { + $.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' ); + } ); + }, 3500); + /* */ + + $(".movement .home").click( function() { + axis = $(this).attr( "data-axis" ); + + if (axis == 'all') { + code = 'G28 X0 Y0 Z0'; + comment = 'all axes'; + } else { + code = 'G28 ' + axis; + comment = axis + ' axis'; + } + + sendCmd( code, 'Home ' + comment ); + } ); + + $(".movement .direction button").click( function() { + command = 'G1 '; + movement = $(this).attr( "data-movement" ); + rate = $(".movement .rate button.active").attr( "data-rate" ); + axis = $(this).attr( "data-axis" ); + comment = 'Move ' + axis; + + if ( movement == 'up' || movement == 'left' ) { rate = rate * -1; } + if ( axis == 'Z' && movement == 'down' ) { comment = 'Raise Z '; } + if ( axis == 'Z' && movement == 'up' ) { comment = 'Lower Z '; } + if ( axis == 'E' && movement == 'plus' ) { comment = 'Extrude '; } + if ( axis == 'E' && movement == 'minus' ) { + sendCmd( command + axis + '-' + rate, 'Retract ' + rate + 'mm' ); + return false; + } + if ( movement == 'disable' ) { + sendCmd( 'M18', 'Disable motor lock' ); + return false; + } + + 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(); + if ( gCode2Send == '' ) { return; } + + sendCmd( gCode2Send, '' ); + $('#gcode').val( '' ); + } ); + + $("#wre").change( function() { + var value = pad( $("#wre").val(), 3 ); + sendCmd( '{C:T0' + value + '}', 'Set extruder preheat to ' + $("#wre").val() + '°C', 'cmd' ); + } ); + + $("#sete").click( function() { + var value = pad( $("#wre").val(), 3 ); + sendCmd( '{C:T0' + value + '}', '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 : true, orientation: 'vertical', + formatter: function(value) { + return value + '%'; + } + } ); + + $("#fanspeed").on( 'slide', function( slideEvt ) { + delaySendSpeed( slideEvt.value ); + } ); + + $("#clrfan").click( function() { + sendCmd( 'M106 S0', 'Turn off fan' ); + } ); + + $("form").submit( function() { + return false; + } ); +} ); + +var timers = {}; + +function pad( num, size ) { + s = '000' + num; + return s.substr( s.length-size ); +} + +function scrollConsole() { + $cont = $("#console"); + $cont[0].scrollTop = $cont[0].scrollHeight; +} + +function sendCmd( cmd, comment, type) { + if (type === undefined) { type = "code"; } + clearTimeout( timers ); + timers = setTimeout( function() { + $("#gCodeLog").append( '

' + cmd + ' ; ' + comment +'

' ); + + $.ajax({ url: 'set?' + type + '=' + cmd, cache: false }).done( function( data ) { + $("#gCodeLog").append( '

' + data + '

' ); + scrollConsole(); + } ); + + scrollConsole(); + }, 300 ); +} + +function feedback( output ) { + $("#gCodeLog").append( '

' + output + '

' ); + scrollConsole(); +} + +String.prototype.contains = function( it ) { + return this.indexOf( it ) != -1; +}; + +Dropzone.options.mydz = { dictDefaultMessage: "Upload G-code Here",accept: function( file, done ) { + if ( file.name.contains( '.g' ) ) done(); + else done( 'Not a valid G-code file.' ); + }, init: function() { + this.on( 'error', function( file, response ) { + var errorMessage = response.errorMessage; + $( file.previewElement ).find( '.dz-error-message' ).text( errorMessage ); + } ); + + this.on( 'addedfile', function() { + if ( this.files[1] != null ) { + this.removeFile( this.files[0] ); + } + } ); + } +}; + +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( 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 ); +} + +function delaySyncTemperatures( extruder, platform ) { + clearTimeout( timers ); + timers = setTimeout( function() { + if ( extruder != 0 ) { $("#wre").val( extruder ); } + if ( platform != 0 ) { $("#wrp").val( platform ); } + }, 3000 ); +} diff --git a/source/webui.min.css b/source/webui.min.css new file mode 100644 index 0000000..88fdf8c --- /dev/null +++ b/source/webui.min.css @@ -0,0 +1 @@ +.dropzone,.webcam,h3{margin-bottom:20px}html{font-size:16px}body{font-family:'Roboto Condensed',sans-serif;font-size:1rem;font-weight:400;line-height:1.5rem;color:#646C7F}h3,h4{color:#0272b6;font-weight:300}h3{font-size:1.5rem}h4{margin-top:0}.btn{letter-spacing:.025rem}.btn-default{color:#646C7F}.btn-disable{pointer-events:none;opacity:.4}footer{padding-top:15px;padding-bottom:10px;font-size:.875rem;text-align:right}@media screen and (max-width :767px){footer.navbar-fixed-bottom{position:relative;text-align:center}}.progress{height:25px}.progress .progress-bar{padding-top:3px}.webcam img{width:auto;height:255px}.print-actions button{margin-bottom:20px;width:100%}.dropzone{min-height:201px;padding:10px;background-color:#fff;border:2px dashed #0272b6;border-radius:5px;color:#00619a}.dropzone .dz-message{top:50%;left:0;position:absolute;width:100%;margin:0;font-weight:500;font-size:1.25rem;text-align:center}.dropzone .dz-preview{width:100%;margin:0}.dropzone .dz-preview .dz-image{display:none}.dropzone .dz-preview .dz-details{top:50%;color:inherit;font-size:.875rem}.dropzone .dz-preview .dz-progress{left:0;width:100%;margin-left:0}.dropzone .dz-preview .dz-error-mark,.dropzone .dz-preview .dz-success-mark{top:40%}.dropzone .dz-preview .dz-success-mark svg g path{fill:#2C9040}.dropzone .dz-preview .dz-error-mark svg g{fill:#FF2F3C}.movement .row{margin-bottom:14px}#gCodeInput,.movement .row.rate{margin-bottom:20px}.movement .direction .text-center{line-height:2rem}.rate .center-block{width:196px}#console{min-height:201px;max-height:201px;overflow:scroll;padding:0 8px;font-family:monospace,monospace;font-size:.875rem}#console p{margin:0 0 4px}.slider,.temperature .target{margin-left:auto;margin-right:auto}#console .comment{color:#2C9040}.temperature h3{margin-top:41px;margin-bottom:40px}.temperature .target{width:100px;margin-bottom:30px}.slider{display:block;margin-bottom:20px}.slider.slider-vertical{height:141px}#fanspeed .slider-selection{background:#0272b6}#sete,#setp{cursor:pointer} \ No newline at end of file diff --git a/source/webui.min.js b/source/webui.min.js new file mode 100644 index 0000000..2d37738 --- /dev/null +++ b/source/webui.min.js @@ -0,0 +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(){0!=e&&$("#wre").val(e),0!=t&&$("#wrp").val(t)},3e3)}$(document).ready(function(){sendCmd("M563 S6","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 diff --git a/webui.html b/webui.html index 0188686..5ff8b69 100755 --- a/webui.html +++ b/webui.html @@ -1,242 +1,218 @@ - - - - - - - - - - - - - - - -