From c78ebc90bfe8f4b74c26a03b8283168af3f59a82 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 11 Dec 2020 09:14:52 -0800 Subject: [PATCH] Multi File Upload, offsets fixed, progress bars --- css/dark.css | 33 +++++++-- css/light.css | 30 ++++++-- css/style.css | 68 ++++++++++++++--- index.html | 58 +++++++++++++-- js/script.js | 200 +++++++++++++++++++++++++++++++++++--------------- 5 files changed, 300 insertions(+), 89 deletions(-) diff --git a/css/dark.css b/css/dark.css index 6dd0695..ded6fcd 100644 --- a/css/dark.css +++ b/css/dark.css @@ -33,28 +33,40 @@ input, select, button { background-color: #fff; } + +#commands button { + border-color: #fff; + background-color: #333; + color: #fff; +} + +#commands button:hover { + background-color: #fff; + color: #333; +} + #notSupported { background-color: red; color: white; } -#firmware + label { +.firmware { border-color: #fff; color: #fff; } -#firmware:focus + label, -#firmware + label:hover { +.firmware:focus, +.firmware:hover { background-color: #fff; color: #333; } -#firmware:disabled + label { +.firmware:disabled { color: #ccc; border-color: #ccc; } -#firmware:disabled + label:hover { +.firmware:disabled:hover { background-color: #fff; } @@ -62,3 +74,14 @@ input { background-color: #fff; color: #333; } + +#commands .buttons button:disabled, +#commands .buttons button:disabled:hover{ + border-color: #999; + background-color: #888; + color: #ccc; +} + +#commands .buttons button:hover { + background-color: #fff; +} diff --git a/css/light.css b/css/light.css index 24344b1..9256353 100644 --- a/css/light.css +++ b/css/light.css @@ -38,22 +38,38 @@ input, select, button { background-color: #333; } -#firmware + label { +.firmware { border-color: #63338f; color: #63338f; } -#firmware:focus + label, -#firmware + label:hover { +.firmware:focus, +.firmware:hover { background-color: #63338f; color: #fff; } -#firmware:disabled + label { - color: #ccc; +.firmware:disabled { + background-color: #ddd; + color: #888; +} + +.firmware:disabled:hover { + background-color: #ddd; +} + +#commands .buttons button { + border-color: #333; +} + +#commands .buttons button:disabled, +#commands .buttons button:disabled:hover { border-color: #ccc; + background-color: #ddd; + color: #888; } -#firmware:disabled + label:hover { - background-color: #fff; +#commands .buttons button:hover { + background-color: #333; + color: #fff; } diff --git a/css/style.css b/css/style.css index 8c0becd..a161037 100644 --- a/css/style.css +++ b/css/style.css @@ -34,7 +34,7 @@ color: #000; } -button, #firmware + label { +button, .firmware { height: 25px; font-size: 16px; border-radius: 15px; @@ -110,11 +110,11 @@ div.clear { } #app.connected #commands { - height: 80px; + height: 200px; } #app.connected #log { - height: calc(100vh - 410px); + height: calc(100vh - 530px); } #app #commands { @@ -287,13 +287,29 @@ div.clear { } #commands { - display: flex; + min-width: 600px; justify-content: center; position: relative; align-items: center; } -#firmware { +#commands .upload { + width: 600px; + display: flex; + align-items: center; + margin: 5px auto; + justify-content: center; +} + +#commands .upload .offset { + width: 50px; +} + +#commands .upload label { + white-space: nowrap; +} + +.firmware > input { width: 0.1px; height: 0.1px; opacity: 0; @@ -302,16 +318,17 @@ div.clear { z-index: -1; } -#firmware + label { +.firmware { border-style: solid; margin-left: 20px; - display:flex; + margin-right: 20px; + display: flex; align-items: center; - cursor: pointer; /* "hand" cursor */ + cursor: pointer; } -#firmware + label span { - margin-left: 10px; +.firmware > svg { + margin-right: 10px; } .debug-function { @@ -325,3 +342,34 @@ div.clear { .timestamp { color: #8ec641; } + +.progress-bar { + width: 100%; + height: 24px; + border-style: solid; + border-width: 2px; + border-color: #333; + border-radius: 10px; + padding: 0; + overflow: hidden; +} + +.progress-bar > div { + height: 24px; + background-color: #71ae1e; + width: 0; +} + +#commands .buttons { + display: flex; + justify-content: center; + width: 600px; + margin: 10px auto; +} + +#commands .buttons button { + margin-left: 10px; + margin-right: 10px; + border-width: 2px; + border-style: solid; +} diff --git a/index.html b/index.html index 52da8cd..c8150c3 100644 --- a/index.html +++ b/index.html @@ -57,16 +57,58 @@
-
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
- - - -
diff --git a/js/script.js b/js/script.js index 20e9417..96649bb 100644 --- a/js/script.js +++ b/js/script.js @@ -72,18 +72,20 @@ const bufferSize = 512; const colors = ['#00a7e9', '#f89521', '#be1e2d']; const measurementPeriodId = '0001'; -const maxLogLength = 500; +const maxLogLength = 100; const log = document.getElementById('log'); const butConnect = document.getElementById('butConnect'); const baudRate = document.getElementById('baudRate'); const butClear = document.getElementById('butClear'); -const offset = document.getElementById('offset'); const butErase = document.getElementById('butErase'); +const butProgram = document.getElementById('butProgram'); const autoscroll = document.getElementById('autoscroll'); const lightSS = document.getElementById('light'); const darkSS = document.getElementById('dark'); const darkMode = document.getElementById('darkmode'); -const firmware = document.getElementById('firmware'); +const firmware = document.querySelectorAll(".upload .firmware input"); +const progress = document.querySelectorAll(".upload .progress-bar"); +const offsets = document.querySelectorAll('.upload .offset'); const appDiv = document.getElementById('app'); const butRemix = document.querySelector(".remix button"); @@ -105,11 +107,17 @@ document.addEventListener('DOMContentLoaded', () => { }); butClear.addEventListener('click', clickClear); butErase.addEventListener('click', clickErase); + butProgram.addEventListener('click', clickProgram); + for (let i = 0; i < firmware.length; i++) { + firmware[i].addEventListener('change', checkFirmware); + } + for (let i = 0; i < offsets.length; i++) { + offsets[i].addEventListener('change', checkProgrammable); + } autoscroll.addEventListener('click', clickAutoscroll); baudRate.addEventListener('change', changeBaudRate); darkMode.addEventListener('click', clickDarkMode); butRemix.addEventListener('click', remix); - firmware.addEventListener('change', uploadFirmware); window.addEventListener('error', function(event) { console.log("Got an uncaught error: ", event.error) }); @@ -173,21 +181,6 @@ function toByteArray(str) { let charcode = str.charCodeAt(i); if (charcode <= 0xFF) { byteArray.push(charcode); - } else if (charcode < 0x800) { - byteArray.push(0xc0 | (charcode >> 6), - 0x80 | (charcode & 0x3f)); - } else if (charcode < 0xd800 || charcode >= 0xe000) { - byteArray.push(0xe0 | (charcode >> 12), - 0x80 | ((charcode>>6) & 0x3f), - 0x80 | (charcode & 0x3f)); - } else { - i++; - charcode = 0x10000 + (((charcode & 0x3ff) << 10) - | (str.charCodeAt(i) & 0x3ff)); - byteArray.push(0xf0 | (charcode >>18), - 0x80 | ((charcode>>12) & 0x3f), - 0x80 | ((charcode>>6) & 0x3f), - 0x80 | (charcode & 0x3f)); } } return byteArray; @@ -413,14 +406,122 @@ async function clickDarkMode() { * Click handler for the erase button. */ async function clickErase() { + if (window.confirm("This will erase the entire flash. Click OK to continue.")) { + baudRate.disabled = true; + butErase.disabled = true; + butProgram.disabled = true; + try { + logMsg("Erasing flash memory. Please wait..."); + let stamp = Date.now(); + await stubLoader.eraseFlash(); + logMsg("Finished. Took " + (Date.now() - stamp) + "ms to erase."); + } catch(e) { + errorMsg(e); + } finally { + butErase.disabled = false; + baudRate.disabled = false; + butProgram.disabled = getValidFiles().length == 0; + } + } +} + +/** + * @name clickProgram + * Click handler for the program button. + */ +async function clickProgram() { + const readUploadedFileAsArrayBuffer = (inputFile) => { + const reader = new FileReader(); + + return new Promise((resolve, reject) => { + reader.onerror = () => { + reader.abort(); + reject(new DOMException("Problem parsing input file.")); + }; + + reader.onload = () => { + resolve(reader.result); + }; + reader.readAsArrayBuffer(inputFile); + }); + }; + baudRate.disabled = true; - try { - await stubLoader.eraseFlash(); - } catch(e) { - errorMsg(e); - } finally { - baudRate.disabled = false; + butErase.disabled = true; + butProgram.disabled = true; + for (let i=0; i< 4; i++) { + firmware[i].disabled = true; + offsets[i].disabled = true; + } + for (let file of getValidFiles()) { + progress[file].classList.remove("hidden"); + let binfile = firmware[file].files[0]; + let contents = await readUploadedFileAsArrayBuffer(binfile); + try { + let offset = parseInt(offsets[file].value, 16); + await stubLoader.flashData(contents, offset, file); + await sleep(100); + } catch(e) { + errorMsg(e); + } + } + for (let i=0; i< 4; i++) { + firmware[i].disabled = false; + offsets[i].disabled = false; + progress[i].classList.add("hidden"); + progress[i].querySelector("div").style.width = "0"; } + butErase.disabled = false; + baudRate.disabled = false; + butProgram.disabled = getValidFiles().length == 0; + logMsg("To run the new firmware, please reset your device.") +} + +function getValidFiles() { + // Get a list of file and offsets + // This will be used to check if we have valid stuff + // and will also return a list of files to program + let validFiles = []; + let offsetVals = []; + for (let i=0; i<4; i++) { + let offs = parseInt(offsets[i].value, 16); + if (firmware[i].files.length > 0 && !offsetVals.includes(offs)) { + validFiles.push(i); + offsetVals.push(offs); + } + } + return validFiles; +} + +/** + * @name checkProgrammable + * Check if the conditions to program the device are sufficient + */ +async function checkProgrammable() { + butProgram.disabled = getValidFiles().length == 0; +} + +/** + * @name checkFirmware + * Handler for firmware upload changes + */ +async function checkFirmware(event) { + let filename = event.target.value.split("\\" ).pop(); + let label = event.target.parentNode.querySelector("span"); + let icon = event.target.parentNode.querySelector("svg"); + if (filename != "") { + if (filename.length > 17) { + label.innerHTML = filename.substring(0, 14) + "…"; + } else { + label.innerHTML = filename; + } + icon.classList.add("hidden"); + } else { + label.innerHTML = "Choose a file…"; + icon.classList.remove("hidden"); + } + + await checkProgrammable(); } /** @@ -431,28 +532,6 @@ async function clickClear() { reset(); } -async function uploadFirmware() { - let binfile = firmware.files[0]; - const reader = new FileReader(); - reader.addEventListener('load', async (event) => { - baudRate.disabled = true; - firmware.disabled = true; - let label = firmware.nextElementSibling; - let labelVal = label.innerHTML; - //try { - label.querySelector('span').innerHTML = "Programming..."; - await espTool.flashData(event.target.result, parseInt(offset.value, 16)); - /*} catch(e) { - errorMsg(e); - } finally { - label.innerHTML = labelVal; - baudRate.disabled = false; - firmware.disabled = false; - }*/ - }); - reader.readAsArrayBuffer(binfile); -} - function convertJSON(chunk) { try { let jsonObj = JSON.parse(chunk); @@ -464,13 +543,15 @@ function convertJSON(chunk) { function toggleUIToolbar(show) { isConnected = show; + for (let i=0; i< 4; i++) { + progress[i].classList.add("hidden"); + progress[i].querySelector("div").style.width = "0"; + } if (show) { appDiv.classList.add("connected"); } else { appDiv.classList.remove("connected"); } - firmware.disabled = !show; - offset.disabled = !show; butErase.disabled = !show; } @@ -487,7 +568,7 @@ function toggleUIConnected(connected) { function loadAllSettings() { // Load all saved settings or defaults autoscroll.checked = loadSetting('autoscroll', true); - baudRate.value = loadSetting('baudrate', 115200); + baudRate.value = loadSetting('baudrate', 921600); darkMode.checked = loadSetting('darkmode', false); } @@ -517,7 +598,7 @@ class EspLoader { this._chipfamily = null; this._efuses = new Array(4).fill(0); this._flashsize = 4 * 1024 * 1024; - this.debug = true; + this.debug = false; this.IS_STUB = false; } @@ -673,7 +754,6 @@ class EspLoader { */ async checkCommand(opcode, buffer, checksum=0, timeout=DEFAULT_TIMEOUT) { timeout = Math.min(timeout, MAX_TIMEOUT); - debugMsg("Pre-encoded data", buffer); await this.sendCommand(opcode, buffer, checksum); let [value, data] = await this.getResponse(opcode, timeout); let statusLen; @@ -1011,7 +1091,7 @@ class EspLoader { * verify memory. ESP8266 does not have checksum memory verification in * ROM */ - async flashData(binaryData, offset=0) { + async flashData(binaryData, offset=0, part=0) { let filesize = binaryData.byteLength; logMsg("\nWriting data with filesize:" + filesize); @@ -1020,14 +1100,17 @@ class EspLoader { let seq = 0; let written = 0; let address = offset; - let position = offset; + let position = 0; let stamp = Date.now(); let flashWriteSize = this.getFlashWriteSize(); + let progressBar = progress[part].querySelector("div"); while (filesize - position > 0) { - logMsg( - "Writing at " + toHex(address + seq * flashWriteSize, 8) + "... (" + Math.floor(100 * (seq + 1) / blocks)+ " %)" - ); + let percentage = Math.floor(100 * (seq + 1) / blocks); + /*logMsg( + "Writing at " + toHex(address + seq * flashWriteSize, 8) + "... (" + percentage + " %)" + );*/ + progressBar.style.width = percentage + "%"; if (filesize - position >= flashWriteSize) { block = Array.from(new Uint8Array(binaryData, position, flashWriteSize)); } else { @@ -1041,7 +1124,6 @@ class EspLoader { position += flashWriteSize; } logMsg("Took " + (Date.now() - stamp) + "ms to write " + filesize + " bytes"); - logMsg("To run the new firmware, please reset your device.") }; /** @@ -1253,7 +1335,7 @@ class EspLoader { if (p != 'OHAI') { throw "Failed to start stub. Unexpected response: " + p; } - logMsg("Stub running..."); + logMsg("Stub is now running..."); return new EspStubLoader(); } }