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();
}
}