diff --git a/cpinstaller/src/installer.js b/cpinstaller/src/base_installer.js
similarity index 100%
rename from cpinstaller/src/installer.js
rename to cpinstaller/src/base_installer.js
diff --git a/cpinstaller/src/cpinstaller.js b/cpinstaller/src/cpinstaller.js
index d9f0fea7..dc47f159 100644
--- a/cpinstaller/src/cpinstaller.js
+++ b/cpinstaller/src/cpinstaller.js
@@ -3,7 +3,7 @@ import { html } from 'https://unpkg.com/lit-html?module';
import * as zip from "https://deno.land/x/zipjs/index.js";
import * as esptoolPackage from "https://unpkg.com/esp-web-flasher@5.1.2/dist/web/index.js?module"
import {REPL} from 'https://cdn.jsdelivr.net/gh/adafruit/circuitpython-repl-js@1.0.0/repl.js';
-import { InstallButton } from "./installer.js";
+import { InstallButton } from "./base_installer.js";
// TODO: Figure out how to make the Web Serial from ESPTool and Web Serial to communicate with CircuitPython not conflict
// I think at the very least we'll have to reuse the same port so the user doesn't need to reselct, though it's possible it
diff --git a/cpinstaller/wsinstaller.js b/cpinstaller/wsinstaller.js
deleted file mode 100644
index 940500ac..00000000
--- a/cpinstaller/wsinstaller.js
+++ /dev/null
@@ -1,1002 +0,0 @@
-'use strict';
-
-import * as esptoolPackage from "https://unpkg.com/esp-web-flasher@5.1.2/dist/web/index.js?module"
-
-// TODO: Figure out how to make the Web Serial from ESPTool and Web Serial to communicate with CircuitPython not conflict
-// I think at the very least we'll have to reuse the same port so the user doesn't need to reselct, though it's possible it
-// may change after reset. Since it's not
-//
-// For now, we'll use the following procedure for ESP32-S2 and ESP32-S3:
-// 1. Install the bin file
-// 2. Reset the board
-// (if version 8.0.0-beta.6 or later)
-// 3. Generate the settings.toml file
-// 4. Write the settings.toml to the board via the REPL
-// 5. Reset the board again
-//
-// For the esp32 and esp32c3, the procedure may be slightly different and going through the
-// REPL may be required for the settings.toml file.
-// 1. Install the bin file
-// 2. Reset the board
-// (if version 8.0.0-beta.6 or later)
-// 3. Generate the settings.toml file
-// 4. Write the settings.toml to the board via the REPL
-// 5. Reset the board again
-//
-// To run REPL code, I may need to modularize the work I did for code.circuitpython.org
-// That allows you to run code in the REPL and get the output back. I may end up creating a
-// library that uses Web Serial and allows you to run code in the REPL and get the output back
-// because it's very integrated into the serial recieve and send code.
-//
-
-let espStub;
-
-const baudRates = [
- 115200,
- 230400,
- 460800,
- 921600,
-];
-
-const stage_erase_all = 0x01;
-const stage_flash_cpbin = 0x02;
-const stage_flash_bootloader = 0x03;
-const stage_copy_uf2 = 0x04;
-const stage_program_settings = 0x05;
-
-const full_bin_program = [stage_erase_all, stage_flash_cpbin, stage_program_settings];
-const full_uf2_program = [stage_erase_all, stage_flash_bootloader, stage_copy_uf2, stage_program_settings];
-const factory_reset_program = [stage_erase_all, stage_flash_bootloader];
-
-// Wizard screens
-// - Menu
-// - Verify user wants to install
-// - erase flash
-// - if esp32 or c3 flash bin
-// - if s2 or s3, flash bootloader
-// - if s2 or s3, copy uf2 (May need to use File System Access API)
-// - request wifi credentials (skip, connect buttons) and AP password
-// - generate and program settings.toml via REPL
-// - install complete
-
-// So we will have a couple of wizard flows and we'll need to associate the flow with the board
-// We should add the info to the board's page in the boards folder
-
-// Changes to make:
-// Hide the log and make it accessible via the menu
-// Generate dialogs on the fly
-// Make a drop-in component
-// Keep in mind it will be used for LEARN too
-// May need to deal with CORS issues
-// May need to deal with the fact that the ESPTool uses Web Serial and CircuitPython REPL uses Web Serial
-
-
-const maxLogLength = 100;
-const installerDialog = document.getElementById('installerDialog');
-const butInstallers = document.getElementsByClassName("installer-button");
-
-const log = installerDialog.querySelector("#log");
-const semverLabel = installerDialog.querySelector("#semver");
-//const butShowConsole = installerDialog.querySelector("#butShowConsole");
-const consoleItems = installerDialog.getElementsByClassName("console-item");
-const butConnect = installerDialog.querySelector("#butConnect");
-const binSelector = installerDialog.querySelector("#binSelector");
-const baudRate = installerDialog.querySelector("#baudRate");
-const butClear = installerDialog.querySelector("#butClear");
-const butProgram = installerDialog.querySelector("#butProgram");
-const butProgramBootloader = installerDialog.querySelector("#butProgramBootloader");
-const autoscroll = installerDialog.querySelector("#autoscroll");
-
-// TODO: This should grab the stuff for settings.toml
-const partitionData = installerDialog.querySelectorAll(".field input.partition-data");
-const progress = installerDialog.querySelector("#progressBar");
-const stepname = installerDialog.querySelector("#stepname");
-const appDiv = installerDialog.querySelector("#app");
-
-const disableWhileBusy = [partitionData, butProgram, butProgramBootloader, baudRate];
-
-let showConsole = false;
-let debug;
-
-// querystring options
-const QUERYSTRING_BOARD_KEY = 'board'
-const QUERYSTRING_DEBUG_KEY = 'debug'
-
-function getFromQuerystring(key) {
- const location = new URL(document.location)
- const params = new URLSearchParams(location.search)
- return params.get(key)
-}
-
-for (let installer of butInstallers) {
- installer.addEventListener("click", (e) => {
- let installerName = e.target.id;
- installerDialog.showModal();
- e.preventDefault();
- e.stopImmediatePropagation();
- });
-}
-
-document.addEventListener("DOMContentLoaded", () => {
- // detect debug setting from querystring
- debug = getFromQuerystring(QUERYSTRING_DEBUG_KEY);
- var getArgs = {};
- location.search
- .substr(1)
- .split("&")
- .forEach(function (item) {
- getArgs[item.split("=")[0]] = item.split("=")[1];
- });
- if (getArgs["debug"] !== undefined) {
- debug = getArgs["debug"] == "1" || getArgs["debug"].toLowerCase() == "true";
- }
-
- /*butShowConsole.addEventListener("click", () => {
- showConsole = !showConsole
- saveSetting("showConsole", showConsole)
- toggleConsole(showConsole)
- })*/
-
- // register dom event listeners
- butConnect.addEventListener("click", () => {
- clickConnect().catch(async (e) => {
- // Default Help Message:
- // if we've failed to catch the message before now, we need to give
- // the generic advice: reconnect, refresh, go to support
- errorMsg(
- `Connection Error, your board may be incompatible. Things to try:\n` +
- `1. Reset your board and try again.\n` +
- ` - Look for a little black button near the power port.\n` +
- `2. Refresh your browser and try again.\n` +
- `3. Make sure you are not connected in another browser tab.\n` +
- `4. Double-check your board type and serial port selection.\n` +
- `5. Post on the Support Forum (link above) with this info:\n\n` +
- `"Firmware Tool: ${e}"\n`
- );
- await disconnect();
- toggleUIConnected(false);
- });
- });
- //butClear.addEventListener("click", clickClear);
- butProgram.addEventListener("click", clickProgram);
- butProgramBootloader.addEventListener("click", clickProgramNvm);
- for (let i = 0; i < partitionData.length; i++) {
- partitionData[i].addEventListener("change", checkProgrammable);
- partitionData[i].addEventListener("keydown", checkProgrammable);
- partitionData[i].addEventListener("input", checkProgrammable);
- }
- //autoscroll.addEventListener("click", clickAutoscroll);
- //baudRate.addEventListener("change", changeBaudRate);
-
- // handle runaway errors
- window.addEventListener("error", event => {
- console.warn(`Uncaught error: ${event.error}`);
- });
-
- // handle runaway rejections
- window.addEventListener("unhandledrejection", event => {
- console.warn(`Unhandled rejection: ${event.reason}`);
- });
-
- // WebSerial feature detection
- if ("serial" in navigator) {
- const notSupported = document.getElementById("notSupported");
- notSupported.classList.add("hidden");
- }
-
- //initBinSelector();
- //initBaudRate();
- loadAllSettings();
- logMsg("CircuitPython ESP32 Installer loaded.");
- checkProgrammable();
-});
-
-function createOption(value, text) {
- const option = document.createElement("option");
- option.text = text;
- option.value = value;
- return option;
-}
-
-let latestFirmwares = []
-/*async function initBinSelector() {
- // fetch firmware index from io-rails, a list of available littlefs
- // firmware items, like the example above
- const response = await fetch(`${FIRMWARE_API}/wipper_releases`)
- // extract the semver from the custom header
- if (!initSemver(response.headers.get('AIO-WS-Firmware-Semver'))) {
- console.error("No semver information in the response headers!")
- }
- // parse and store firmware data for reuse
- latestFirmwares = await(response.json())
-
- // populate the bin select element
- populateBinSelector("Click Here to Find Your Board:")
-
- // pull default board id out of querystring
- if (setDefaultBoard()) {
- // inject board name into alternate step 1
- const boardNameItems = document.getElementsByClassName('selected-board-name')
- for (let idx = 0; idx < boardNameItems.length; idx++) {
- boardNameItems[idx].innerHTML = binSelector.selectedOptions[0].text;
- }
- // show alternate step 1
- showAltStepOne()
- } else {
- binSelector.addEventListener("change", changeBin);
- }
-}
-
-function populateBinSelector(title, filter=() => true) {
- binSelector.innerHTML = '';
-
- const filteredFirmwares = latestFirmwares.filter(filter)
- const any = filteredFirmwares.length > 0
-
- binSelector.add(createOption(null, any ? title : 'No Compatible Boards'))
-
- filteredFirmwares.forEach(firmware => {
- binSelector.add(createOption(firmware.id, firmware.name));
- })
-
- return any
-}*/
-
-function returnToStepOne() {
- showStep(1, { hideHigherSteps: false });
- doThingOnClass("add", "dimmed", "step-2")
- // yellow fade like 2005
- setTimeout(() => doThingOnClass("add", "highlight", "step-1"), 0)
- setTimeout(() => doThingOnClass("remove", "highlight", "step-1"), 1500)
- doThingOnClass("add", "hidden", "step-1 alt")
-}
-
-function showAltStepOne() {
- doThingOnClass("add", "hidden", "step-1")
- doThingOnClass("remove", "hidden", "step-1 alt")
-}
-
-function doThingOnClass(method, thing, classSelector) {
- const classItems = document.getElementsByClassName(classSelector)
- for (let idx = 0; idx < classItems.length; idx++) {
- classItems.item(idx).classList[method](thing)
- }
-}
-
-function setDefaultBoard() {
- const board = getFromQuerystring(QUERYSTRING_BOARD_KEY)
- if (board && hasBoard(board)) {
- binSelector.value = board
- showStep(2, { dimLowerSteps: false })
- return true
- }
-}
-
-function hasBoard(board) {
- for (let opt of binSelector.options) {
- if (opt.value == board) { return opt }
- }
-}
-
-function changeBin(evt) {
- (evt.target.value && evt.target.value != "null") ?
- showStep(2) :
- hideStep(2)
-}
-
-function showStep(stepNumber, options={}) {
- const dimLowerSteps = !(options.dimLowerSteps === false)
- const hideHigherSteps = !(options.hideHigherSteps === false)
- // reveal the new step
- doThingOnClass("remove", "hidden", `step-${stepNumber}`)
- doThingOnClass("remove", "dimmed", `step-${stepNumber}`)
-
- if (dimLowerSteps) {
- for (let step = stepNumber - 1; step > 0; step--) {
- doThingOnClass("add", "dimmed", `step-${step}`)
- }
- }
-
- if (hideHigherSteps) {
- for (let step = stepNumber + 1; step <= 6; step++) {
- doThingOnClass("add", "hidden", `step-${step}`)
- }
- }
-
- // per-step things, like a state machine
- switch(stepNumber) {
- case 3:
- checkProgrammable()
- break;
- case 4:
- butProgram.disabled = false
- butProgramNvm.disabled = false
- break;
- }
-
- // scroll to the bottom next frame
- setTimeout((() => appDiv.scrollTop = appDiv.scrollHeight), 0)
-}
-
-function hideStep(stepNumber) {
- doThingOnClass("add", "hidden", `step-${stepNumber}`)
-}
-
-function toggleConsole(show) {
- // hide/show the console log and its widgets
- const consoleItemsMethod = show ? "remove" : "add"
- for (let idx = 0; idx < consoleItems.length; idx++) {
- consoleItems.item(idx).classList[consoleItemsMethod]("hidden")
- }
- // toggle the button
- //butShowConsole.checked = show
- // tell the app if it's sharing space with the console
- const appDivMethod = show ? "add" : "remove"
- appDiv.classList[appDivMethod]("with-console")
-
- // scroll both to the bottom a moment after adding
- setTimeout(() => {
- log.scrollTop = log.scrollHeight
- appDiv.scrollTop = appDiv.scrollHeight
- }, 200)
-}
-
-let semver
-function initSemver(newSemver) {
- if (!newSemver) { return }
-
- semver = newSemver
- semverLabel.innerHTML = semver
-
- return true
-}
-
-function lookupFirmwareByBinSelector() {
- // get the currently selected board id
- const selectedId = binSelector.value
- if (!selectedId || selectedId === 'null') { throw new Error("No board selected.") }
-
- // grab the stored firmware settings for this id
- let selectedFirmware
- for (let firmware of latestFirmwares) {
- if (firmware.id === selectedId) {
- selectedFirmware = firmware
- break
- }
- }
-
- if (!selectedFirmware) {
- const { text, value } = binSelector.selectedOptions[0]
- throw new Error(`No firmware entry for: ${text} (${value})`)
- }
-
- return selectedFirmware
-}
-
-function initBaudRate() {
- for (let rate of baudRates) {
- baudRate.add(createOption(rate, `${rate} Baud`));
- }
-}
-
-let lastPercent = 0;
-
-/**
- * @name disconnect
- * Closes the Web Serial connection.
- */
-async function disconnect() {
- toggleUIToolbar(false);
- if (espStub) {
- await espStub.disconnect();
- await espStub.port.close();
- toggleUIConnected(false);
- espStub = undefined;
- }
-}
-
-function logMsg(text) {
- log.innerHTML += text.replaceAll("\n", "
") + "
";
-
- // Remove old log content
- if (log.textContent.split("\n").length > maxLogLength + 1) {
- let logLines = log.innerHTML.replace(/(\n)/gm, "").split("
");
- log.innerHTML = logLines.splice(-maxLogLength).join("
\n");
- }
-
- /*if (autoscroll.checked) {
- log.scrollTop = log.scrollHeight;
- }*/
-}
-
-function debugMsg(...args) {
- function getStackTrace() {
- let stack = new Error().stack;
- stack = stack.split("\n").map((v) => v.trim());
- for (let i = 0; i < 3; i++) {
- stack.shift();
- }
-
- let trace = [];
- for (let line of stack) {
- line = line.replace("at ", "");
- trace.push({
- func: line.substr(0, line.indexOf("(") - 1),
- pos: line.substring(line.indexOf(".js:") + 4, line.lastIndexOf(":")),
- });
- }
-
- return trace;
- }
-
- let stack = getStackTrace();
- stack.shift();
- let top = stack.shift();
- let prefix = '[' + top.func + ":" + top.pos + "] ";
- for (let arg of args) {
- if (typeof arg == "string") {
- logMsg(prefix + arg);
- } else if (typeof arg == "number") {
- logMsg(prefix + arg);
- } else if (typeof arg == "boolean") {
- logMsg(prefix + arg ? "true" : "false");
- } else if (Array.isArray(arg)) {
- logMsg(prefix + "[" + arg.map((value) => espStub.toHex(value)).join(", ") + "]");
- } else if (typeof arg == "object" && arg instanceof Uint8Array) {
- logMsg(
- prefix +
- "[" +
- Array.from(arg)
- .map((value) => espStub.toHex(value))
- .join(", ") +
- "]"
- );
- } else {
- logMsg(prefix + "Unhandled type of argument:" + typeof arg);
- console.log(arg);
- }
- prefix = ""; // Only show for first argument
- }
-}
-
-function errorMsg(text, forwardLink=null) {
- // regular log with red Error: prefix
- logMsg(' ' + text);
- // strip html for console and alerts
- const strippedText = text.replaceAll(/<.*?>/g, "")
- // all errors go to the browser dev console
- console.error(strippedText);
- // Make sure user sees the error if the log is closed
- if (!showConsole) {
- if (forwardLink) {
- if (confirm(`${strippedText}\nClick 'OK' to be forwarded there now.`)) {
- document.location = forwardLink
- }
- } else {
- alert(strippedText)
- }
- }
-}
-
-function formatMacAddr(macAddr) {
- return macAddr.map((value) => value.toString(16).toUpperCase().padStart(2, "0")).join(":");
-}
-
-/**
- * @name reset
- * Reset the Panels, Log, and associated data
- */
-async function reset() {
- // Clear the log
- log.innerHTML = "";
-}
-
-/**
- * @name clickConnect
- * Click handler for the connect/disconnect button.
- */
-async function clickConnect() {
- await disconnect();
-
- butConnect.textContent = "Connecting...";
- butConnect.disabled = true
-
- const esploader = await esptoolPackage.connect({
- log: (...args) => logMsg(...args),
- debug: debug ? (...args) => debugMsg(...args) : (...args) => {},
- error: (...args) => errorMsg(...args),
- });
-
- try {
- await esploader.initialize();
-
- const chipType = esploader.chipFamily;
- const chipName = esploader.chipName;
- toggleUIConnected(true);
- toggleUIToolbar(true);
- appDiv.classList.add("connected");
-
- logMsg("Connected to " + esploader.chipName);
- logMsg("MAC Address: " + formatMacAddr(esploader.macAddr()));
-
- const nextStepCallback = async () => {
- showStep(3)
- espStub = await esploader.runStub();
- espStub.addEventListener("disconnect", () => {
- toggleUIConnected(false);
- espStub = false;
- });
- await setBaudRateIfChipSupports(chipType);
- }
-
- // check chip compatibility
- if (checkChipTypeMatchesSelectedBoard(chipType)) {
- await nextStepCallback()
- return
- }
-
- // not compatible, grab the board name for messaging...
- const boardName = lookupFirmwareByBinSelector().name
- // ...and reset the selector to only compatible boards, if any!
- const any = populateBinSelector(`Possible ${chipName} Boards:`, firmware => {
- return (BOARD_TO_CHIP_MAP[firmware.id] == chipType)
- })
-
- if (any) {
- // there are compatible boards available
- // reset the bin selector
- binSelector.disabled = false
- binSelector.removeEventListener("change", changeBin);
- binSelector.addEventListener("change", async evt => {
- // upon compatible board selection, reveal next step
- if (evt.target.value && evt.target.value != "null" && checkChipTypeMatchesSelectedBoard(chipType)) {
- logMsg(`Compatible board selected: ${boardName}`)
- await nextStepCallback()
- }
- });
-
- // explain all this to the user
- errorMsg(`Oops, wrong board!\n` +
- `- you selected: ${boardName}\n` +
- `- you connected: ${chipName}\n` +
- `You can:\n` +
- `- go back to Step 1 and select a compatible board\n` +
- `- connect a different board and refresh the browser`)
-
- // reveal step one
- returnToStepOne()
- return
- }
-
- // no compatible boards available
- // explain to the user with a link to the appropriate guide
- errorMsg(`Oops! This tool doesn't support your board, ${chipName}, but WipperSnapper still might!\n` +
- `Visit the quick-start guide for a list of supported boards and their install instructions.`, QUICK_START_LINK)
- // can't use it so disconnect now
- await disconnect()
-
- } catch (err) {
- await esploader.disconnect();
- // Disconnection before complete
- toggleUIConnected(false);
- showStep(2, { hideHigherSteps: true })
- errorMsg("Oops, we lost connection to your board before completing the install. Please check your USB connection and click Connect again. Refresh the browser if it becomes unresponsive.")
- }
-}
-
-function checkChipTypeMatchesSelectedBoard(chipType, boardId=null) {
- // allow overriding which board we're checking against
- boardId = boardId || binSelector.value
- // wrap the lookup
- return (BOARD_TO_CHIP_MAP[boardId] == chipType)
-}
-
-async function setBaudRateIfChipSupports(chipType) {
- const baud = parseInt(baudRate.value);
- if (baud == espStub.ESP_ROM_BAUD) { return } // already the default
-
- if (chipType == espStub.ESP32) { // only supports the default
- logMsg("WARNING: ESP32 is having issues working at speeds faster than 115200. Continuing at 115200 for now...");
- return
- }
-
- await changeBaudRate(baud);
-}
-
-/**
- * @name changeBaudRate
- * Change handler for the Baud Rate selector.
- */
-async function changeBaudRate() {
- saveSetting("baudrate", baudRate.value);
- if (espStub) {
- let baud = parseInt(baudRate.value);
- if (baudRates.includes(baud)) {
- await espStub.setBaudrate(baud);
- }
- }
-}
-
-/**
- * @name clickAutoscroll
- * Change handler for the Autoscroll checkbox.
- */
-async function clickAutoscroll() {
- saveSetting("autoscroll", autoscroll.checked);
-}
-
-/**
- * @name clickProgram
- * Click handler for the program button.
- */
-async function clickProgram() {
- await programScript(full_bin_program);
-}
-
-/**
- * @name clickProgramNvm
- * Click handler for the program button.
- */
-async function clickProgramNvm() {
- await programScript(factory_reset_program);
-}
-
-async function populateSecretsFile(path) {
- let response = await fetch(path);
- let contents = await response.json();
-
- // Get the secrets data
- for (let field of getValidFields()) {
- const { id, value } = partitionData[field]
- if(id === "status_pixel_brightness") {
- const floatValue = parseFloat(value)
- updateObject(contents, id, isNaN(floatValue) ? 0.2 : floatValue);
- } else {
- updateObject(contents, id, value);
- }
- }
-
- // Convert the data to text and return
- return JSON.stringify(contents, null, 4);
-}
-
-function updateObject(obj, path, value) {
- if (typeof obj === "undefined") {
- return false;
- }
-
- var _index = path.indexOf(".");
- if (_index > -1) {
- return updateObject(obj[path.substring(0, _index)], path.substr(_index + 1), value);
- }
-
- obj[path] = value;
-}
-
-
-let chipFiles
-async function fetchFirmwareForSelectedBoard() {
- const firmware = lookupFirmwareByBinSelector()
-
- logMsg(`Fetching latest firmware...`)
- const response = await fetch(`${FIRMWARE_API}/wipper_releases/${firmware.id}`, {
- headers: { Accept: 'application/octet-stream' }
- })
-
- // Zip stuff
- logMsg("Unzipping firmware bundle...")
- const blob = await response.blob()
- const reader = new zip.ZipReader(new zip.BlobReader(blob));
-
- // unzip into local file cache
- chipFiles = await reader.getEntries();
-}
-
-const BASE_SETTINGS = {
- files: [
- {
- filename: "secrets.json",
- callback: populateSecretsFile,
- },
- ],
- rootFolder: "files",
-};
-
-function findInZip(filename) {
- const regex = RegExp(filename.replace("VERSION", "(.*)"))
- for (let i = 0; i < chipFiles.length; i++) {
- if (chipFiles[i].filename.match(regex)) {
- return chipFiles[i]
- }
- }
-}
-
-async function mergeSettings() {
- const { settings } = lookupFirmwareByBinSelector()
-
- const transformedSettings = {
- ...settings,
- // convert the offset value from hex string to number
- offset: parseInt(settings.offset, 16),
- // replace the structure object with one where the keys have been converted
- // from hex strings to numbers
- structure: Object.keys(settings.structure).reduce((newObj, hexString) => {
- // new object, converted key (hex string -> numeric), same value
- newObj[parseInt(hexString, 16)] = settings.structure[hexString]
-
- return newObj
- }, {})
- }
-
- // merge with the defaults and send back
- return {
- ...BASE_SETTINGS,
- ...transformedSettings
- }
-}
-
-async function programScript(stages) {
- butProgram.disabled = true
- butProgramNvm.disabled = true
- try {
- await fetchFirmwareForSelectedBoard()
- } catch(error) {
- errorMsg(error.message)
- return
- }
-
- // pretty print the settings object with VERSION placeholders filled
- const settings = await mergeSettings()
- const settingsString = JSON.stringify(settings, null, 2)
- const strippedSettings = settingsString.replaceAll('VERSION', semver)
- logMsg(`Flashing with settings:
${strippedSettings}`)
-
- let steps = [];
- for (let i = 0; i < stages.length; i++) {
- if (stages[i] == stage_erase_all) {
- steps.push({
- name: "Erasing Flash",
- func: async function () {
- await espStub.eraseFlash();
- },
- params: {},
- });
- } else if (stages[i] == stage_flash_cpbin) {
- for (const [offset, filename] of Object.entries(settings.structure)) {
- steps.push({
- name: "Flashing " + filename.replace('VERSION', semver),
- func: async function (params) {
- const firmware = await getFirmware(params.filename);
- const progressBar = progress.querySelector("div");
- lastPercent = 0;
- await espStub.flashData(
- firmware,
- (bytesWritten, totalBytes
- ) => {
- let percentage = Math.floor((bytesWritten / totalBytes) * 100)
- if (percentage != lastPercent) {
- logMsg(`${percentage}% (${bytesWritten}/${totalBytes})...`);
- lastPercent = percentage;
- }
- progressBar.style.width = percentage + "%";
- },
- params.offset,
- 0
- );
- },
- params: {
- filename: filename,
- offset: offset,
- },
- });
- }
- } else if (stages[i] == stage_flash_bootloader) {
- for (const [offset, filename] of Object.entries(settings.structure)) {
- steps.push({
- name: "Flashing " + filename.replace('VERSION', semver),
- func: async function (params) {
- const firmware = await getFirmware(params.filename);
- const progressBar = progress.querySelector("div");
- lastPercent = 0;
- await espStub.flashData(
- firmware,
- (bytesWritten, totalBytes
- ) => {
- let percentage = Math.floor((bytesWritten / totalBytes) * 100)
- if (percentage != lastPercent) {
- logMsg(`${percentage}% (${bytesWritten}/${totalBytes})...`);
- lastPercent = percentage;
- }
- progressBar.style.width = percentage + "%";
- },
- params.offset,
- 0
- );
- },
- params: {
- filename: filename,
- offset: offset,
- },
- });
- }
- } else if (stages[i] == stage_program_settings) {
- // TODO: This needs to be rewritten to talk with circuitpython
- // and run python code via the repl to write a settings.toml file
- // See https://learn.adafruit.com/circuitpython-with-esp32-quick-start/setting-up-web-workflow
- // and https://github.com/circuitpython/web-editor/pull/46
- steps.push({
- name: "Generating and Writing the WiFi Settings",
- func: async function (params) {
- let fileSystemImage = await generate(params.flashParams);
-
- if (DO_DOWNLOAD) {
- // Download the Partition
- var blob = new Blob([new Uint8Array(fileSystemImage)], {
- type: "application/octet-stream",
- });
- var link = document.createElement("a");
- link.href = window.URL.createObjectURL(blob);
- link.download = "littleFS.bin";
- link.click();
- link.remove();
- } else {
- const progressBar = progress.querySelector("div");
- lastPercent = 0;
- await espStub.flashData(
- new Uint8Array(fileSystemImage).buffer,
- (bytesWritten, totalBytes) => {
- let percentage = Math.floor((bytesWritten / totalBytes) * 100)
- if (percentage != lastPercent) {
- logMsg(`${percentage}% (${bytesWritten}/${totalBytes})...`);
- lastPercent = percentage;
- }
- progressBar.style.width = percentage + "%";
- },
- params.flashParams.offset,
- 0
- );
- }
- },
- params: {
- flashParams: settings,
- },
- });
- }
- }
-
- for (let i = 0; i < disableWhileBusy.length; i++) {
- if (Array.isArray(disableWhileBusy[i])) {
- for (let j = 0; j < disableWhileBusy[i].length; i++) {
- disableWhileBusy[i][j].disable = true;
- }
- } else {
- disableWhileBusy[i].disable = true;
- }
- }
-
- progress.classList.remove("hidden");
- stepname.classList.remove("hidden");
- showStep(5)
-
- for (let i = 0; i < steps.length; i++) {
- stepname.innerText = steps[i].name + " (" + (i + 1) + "/" + steps.length + ")...";
- await steps[i].func(steps[i].params);
- }
-
- stepname.classList.add("hidden");
- stepname.innerText = "";
- progress.classList.add("hidden");
- progress.querySelector("div").style.width = "0";
-
- for (let i = 0; i < disableWhileBusy.length; i++) {
- if (Array.isArray(disableWhileBusy[i])) {
- for (let j = 0; j < disableWhileBusy[i].length; i++) {
- disableWhileBusy[i][j].disable = false;
- }
- } else {
- disableWhileBusy[i].disable = false;
- }
- }
-
- checkProgrammable();
- await disconnect();
- logMsg("To run the new firmware, please reset your device.");
- showStep(6);
-}
-
-function getValidFields() {
- // Validate user inputs
- const validFields = [];
- for (let i = 0; i < 3; i++) {
- const { id, value } = partitionData[i]
- // password & brightness can be blank, the rest must have some value
- if (id === "network_type_wifi.network_password" ||
- value.length > 0) {
- validFields.push(i);
- }
- }
- return validFields;
-}
-
-/**
- * @name checkProgrammable
- * Check if the conditions to program the device are sufficient
- */
-async function checkProgrammable() {
- if (getValidFields().length < 5) {
- hideStep(4)
- } else {
- showStep(4, { dimLowerSteps: false })
- }
-}
-
-/**
- * @name clickClear
- * Click handler for the clear button.
- */
-async function clickClear() {
- reset();
-}
-
-function toggleUIToolbar(show) {
- for (let i = 0; i < 4; i++) {
- progress.classList.add("hidden");
- progress.querySelector("div").style.width = "0";
- }
- if (show) {
- appDiv.classList.add("connected");
- } else {
- appDiv.classList.remove("connected");
- }
-}
-
-function toggleUIConnected(connected) {
- let lbl = "Connect";
- if (connected) {
- lbl = "Connected";
- butConnect.disabled = true
- binSelector.disabled = true
- } else {
- toggleUIToolbar(false);
- butConnect.disabled = false
- binSelector.disabled = false
- }
- butConnect.textContent = lbl;
-}
-
-function loadAllSettings() {
- // Load all saved settings or defaults
- //autoscroll.checked = loadSetting("autoscroll", true);
- //baudRate.value = loadSetting("baudrate", baudRates[0]);
- showConsole = loadSetting('showConsole', false);
- toggleConsole(showConsole);
-}
-
-function loadSetting(setting, defaultValue) {
- return JSON.parse(window.localStorage.getItem(setting)) || defaultValue;
-}
-
-function saveSetting(setting, value) {
- window.localStorage.setItem(setting, JSON.stringify(value));
-}
-
-async function getFirmware(filename) {
- const file = findInZip(filename)
-
- if (!file) {
- const msg = `No firmware file name ${filename} found in the zip!`
- errorMsg(msg)
- throw new Error(msg)
- }
-
- logMsg(`Unzipping ${filename.replace('VERSION', semver)}...`)
- const firmwareFile = await file.getData(new zip.Uint8ArrayWriter())
-
- return firmwareFile.buffer // ESPTool wants an ArrayBuffer
-}
-
-async function getFileText(path) {
- let response = await fetch(path);
- let contents = await response.text();
- return contents;
-}