<!DOCTYPE html> <!-- saved from url=(0031)http://micropython.org/webrepl/ --> <html><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252"><style id="term-style">.terminal { float: left; border: #000000 solid 5px; font-family: "DejaVu Sans Mono", "Liberation Mono", monospace; font-size: 11px; color: #f0f0f0; background: #000000; } .terminal-cursor { color: #000000; background: #f0f0f0; } </style> <title>MicroPython WebREPL</title> <!-- term.js Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) Copyright (c) 2016, Paul Sokolovsky --> <style> html { background: #555; } h1 { margin-bottom: 20px; font: 20px/1.5 sans-serif; } /* .terminal { float: left; border: #000 solid 5px; font-family: "DejaVu Sans Mono", "Liberation Mono", monospace; font-size: 11px; color: #f0f0f0; background: #000; } .terminal-cursor { color: #000; background: #f0f0f0; } */ .file-box { margin: 4px; padding: 4px; background: #888; } </style> <script src="./MicroPython WebREPL_files/term.js"></script> <script src="./MicroPython WebREPL_files/FileSaver.js"></script> </head> <body> <div style="display:inline-block; vertical-align:top;"> <form> <input type="text" name="webrepl_url" id="url" value="ws://192.168.1.102:8266/"> <input type="submit" id="button" value="Connect" onclick="button_click(); return false;"> </form> <div id="term"> <div class="terminal" tabindex="0" spellcheck="false" style="outline: none; background-color: rgb(0, 0, 0); color: rgb(240, 240, 240);"><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div></div></div> </div> <div id="file-boxes" style="display:inline-block; vertical-align:top; width:230px;"> <div class="file-box"> <strong>Send a file</strong> <input type="file" id="put-file-select"> <div id="put-file-list"></div> <input type="button" value="Send to device" id="put-file-button" onclick="put_file(); return false;" disabled=""> </div> <div class="file-box"> <strong>Get a file</strong> <input type="text" name="get_filename" id="get_filename" value="" size="13"> <input type="button" value="Get from device" onclick="get_file(); return false;"> </div> <div class="file-box" id="file-status"><span style="color:#707070">(file operation status)</span></div> </div> <br clear="both"> <i>Terminal widget should be focused (text cursor visible) to accept input. Click on it if not.</i><br> <i>To paste, press Ctrl+A, then Ctrl+V</i> <script> ; var term; var ws; var connected = false; var binary_state = 0; var put_file_name = null; var put_file_data = null; var get_file_name = null; var get_file_data = null; function calculate_size(win) { var cols = Math.max(80, Math.min(150, (win.innerWidth - 280) / 7)) | 0; var rows = Math.max(24, Math.min(80, (win.innerHeight - 180) / 12)) | 0; return [cols, rows]; } (function() { window.onload = function() { var size = calculate_size(self); term = new Terminal({ cols: size[0], rows: size[1], useStyle: true, screenKeys: true, cursorBlink: false }); term.open(document.getElementById("term")); }; window.addEventListener('resize', function() { var size = calculate_size(self); term.resize(size[0], size[1]); }); }).call(this); function button_click() { if (connected) { ws.close(); } else { document.getElementById('url').disabled = true; document.getElementById('button').value = "Disconnect"; connected = true; connect(document.getElementById('url').value); } } function prepare_for_connect() { document.getElementById('url').disabled = false; document.getElementById('button').value = "Connect"; } function update_file_status(s) { document.getElementById('file-status').innerHTML = s; } function connect(url) { ws = new WebSocket(url); ws.binaryType = 'arraybuffer'; ws.onopen = function() { term.removeAllListeners('data'); term.on('data', function(data) { // Pasted data from clipboard will likely contain // LF as EOL chars. data = data.replace(/\n/g, "\r"); ws.send(data); }); term.on('title', function(title) { document.title = title; }); term.focus(); term.element.focus(); term.write('\x1b[31mWelcome to MicroPython!\x1b[m\r\n'); ws.onmessage = function(event) { if (event.data instanceof ArrayBuffer) { var data = new Uint8Array(event.data); switch (binary_state) { case 11: // first response for put if (decode_resp(data) == 0) { // send file data in chunks for (var offset = 0; offset < put_file_data.length; offset += 1024) { ws.send(put_file_data.slice(offset, offset + 1024)); } binary_state = 12; } break; case 12: // final response for put if (decode_resp(data) == 0) { update_file_status('Sent ' + put_file_name + ', ' + put_file_data.length + ' bytes'); } else { update_file_status('Failed sending ' + put_file_name); } binary_state = 0; break; case 21: // first response for get if (decode_resp(data) == 0) { binary_state = 22; var rec = new Uint8Array(1); rec[0] = 0; ws.send(rec); } break; case 22: { // file data var sz = data[0] | (data[1] << 8); if (data.length == 2 + sz) { // we assume that the data comes in single chunks if (sz == 0) { // end of file binary_state = 23; } else { // accumulate incoming data to get_file_data var new_buf = new Uint8Array(get_file_data.length + sz); new_buf.set(get_file_data); new_buf.set(data.slice(2), get_file_data.length); get_file_data = new_buf; update_file_status('Getting ' + get_file_name + ', ' + get_file_data.length + ' bytes'); var rec = new Uint8Array(1); rec[0] = 0; ws.send(rec); } } else { binary_state = 0; } break; } case 23: // final response if (decode_resp(data) == 0) { update_file_status('Got ' + get_file_name + ', ' + get_file_data.length + ' bytes'); saveAs(new Blob([get_file_data], {type: "application/octet-stream"}), get_file_name); } else { update_file_status('Failed getting ' + get_file_name); } binary_state = 0; break; case 31: // first (and last) response for GET_VER console.log('GET_VER', data); binary_state = 0; break; } } term.write(event.data); }; }; ws.onclose = function() { connected = false; if (term) { term.write('\x1b[31mDisconnected\x1b[m\r\n'); } prepare_for_connect(); } } function decode_resp(data) { if (data[0] == 'W'.charCodeAt(0) && data[1] == 'B'.charCodeAt(0)) { var code = data[2] | (data[3] << 8); return code; } else { return -1; } } function put_file() { var dest_fname = put_file_name; var dest_fsize = put_file_data.length; // WEBREPL_FILE = "<2sBBQLH64s" var rec = new Uint8Array(2 + 1 + 1 + 8 + 4 + 2 + 64); rec[0] = 'W'.charCodeAt(0); rec[1] = 'A'.charCodeAt(0); rec[2] = 1; // put rec[3] = 0; rec[4] = 0; rec[5] = 0; rec[6] = 0; rec[7] = 0; rec[8] = 0; rec[9] = 0; rec[10] = 0; rec[11] = 0; rec[12] = dest_fsize & 0xff; rec[13] = (dest_fsize >> 8) & 0xff; rec[14] = (dest_fsize >> 16) & 0xff; rec[15] = (dest_fsize >> 24) & 0xff; rec[16] = dest_fname.length & 0xff; rec[17] = (dest_fname.length >> 8) & 0xff; for (var i = 0; i < 64; ++i) { if (i < dest_fname.length) { rec[18 + i] = dest_fname.charCodeAt(i); } else { rec[18 + i] = 0; } } // initiate put binary_state = 11; update_file_status('Sending ' + put_file_name + '...'); ws.send(rec); } function get_file() { var src_fname = document.getElementById('get_filename').value; // WEBREPL_FILE = "<2sBBQLH64s" var rec = new Uint8Array(2 + 1 + 1 + 8 + 4 + 2 + 64); rec[0] = 'W'.charCodeAt(0); rec[1] = 'A'.charCodeAt(0); rec[2] = 2; // get rec[3] = 0; rec[4] = 0; rec[5] = 0; rec[6] = 0; rec[7] = 0; rec[8] = 0; rec[9] = 0; rec[10] = 0; rec[11] = 0; rec[12] = 0; rec[13] = 0; rec[14] = 0; rec[15] = 0; rec[16] = src_fname.length & 0xff; rec[17] = (src_fname.length >> 8) & 0xff; for (var i = 0; i < 64; ++i) { if (i < src_fname.length) { rec[18 + i] = src_fname.charCodeAt(i); } else { rec[18 + i] = 0; } } // initiate get binary_state = 21; get_file_name = src_fname; get_file_data = new Uint8Array(0); update_file_status('Getting ' + get_file_name + '...'); ws.send(rec); } function get_ver() { // WEBREPL_REQ_S = "<2sBBQLH64s" var rec = new Uint8Array(2 + 1 + 1 + 8 + 4 + 2 + 64); rec[0] = 'W'.charCodeAt(0); rec[1] = 'A'.charCodeAt(0); rec[2] = 3; // GET_VER // rest of "rec" is zero // initiate GET_VER binary_state = 31; ws.send(rec); } function handle_put_file_select(evt) { // The event holds a FileList object which is a list of File objects, // but we only support single file selection at the moment. var files = evt.target.files; // Get the file info and load its data. var f = files[0]; put_file_name = f.name; var reader = new FileReader(); reader.onload = function(e) { put_file_data = new Uint8Array(e.target.result); document.getElementById('put-file-list').innerHTML = '' + escape(put_file_name) + ' - ' + put_file_data.length + ' bytes'; document.getElementById('put-file-button').disabled = false; }; reader.readAsArrayBuffer(f); } document.getElementById('put-file-select').addEventListener('change', handle_put_file_select, false); document.getElementById('put-file-button').disabled = true; </script> </body></html>