Added inital files

This commit is contained in:
Melissa LeBlanc-Williams 2022-02-08 10:44:04 -08:00
parent 9b0b2ad9b2
commit 2dab9c20fa
21 changed files with 4791 additions and 130 deletions

130
.gitignore vendored
View file

@ -1,129 +1 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
*.DS_Store

View file

@ -1,2 +1,7 @@
# WipperSnapper_Firmware_Uploader
# WipperSnapper Firmware Uploader
A JavaScript tool for generating a LittleFS Partition with Credentials for WipperSnapper.
## Live Tool
A live copy of the tool is hosted here: https://adafruit.github.io/WipperSnapper_Firmware_Uploader/

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
bin/boot_app0.bin Normal file

Binary file not shown.

91
css/dark.css Normal file
View file

@ -0,0 +1,91 @@
.header {
background: #000;
color: #fff;
}
body {
background-color: #333;
color: #fff;
}
input, select, button {
background-color: #333;
color: #fff;
}
.footer button {
border-color: #fff;
background-color: #333;
color: #fff;
}
.footer button:hover {
background-color: #fff;
color: #333;
}
.remix button {
border-color: #fff;
color: #fff;
}
.remix button:hover {
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 {
border-color: #fff;
color: #fff;
}
.firmware:focus,
.firmware:hover {
background-color: #fff;
color: #333;
}
.firmware:disabled {
color: #ccc;
border-color: #ccc;
}
.firmware:disabled:hover {
background-color: #fff;
}
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;
}
.progress-bar {
border-color: #aaa;
}

79
css/light.css Normal file
View file

@ -0,0 +1,79 @@
.header {
background: #000;
color: #fff;
}
body {
background-color: #fff;
color: #000;
}
input, select, button {
background-color: #fff;
color: #000;
}
#notSupported {
background-color: red;
color: white;
}
.footer button {
border-color: #63338f;
background-color: #fff;
color: #63338f;
}
.footer button:hover {
background-color: #63338f;
color: #fff;
}
.remix button {
border-color: #333;
color: #333;
}
.remix button:hover {
background-color: #333;
}
.firmware {
border-color: #63338f;
color: #63338f;
}
.firmware:focus,
.firmware:hover {
background-color: #63338f;
color: #fff;
}
.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;
}
#commands .buttons button:hover {
background-color: #333;
color: #fff;
}
.progress-bar {
border-color: #333;
}

376
css/style.css Normal file
View file

@ -0,0 +1,376 @@
/**
* Header
*/
.header {
box-sizing: border-box;
font-size: 16px;
height: 85px;
line-height: 40px;
padding: 20px 70px 0px 70px; /* TRouBLe */
position: fixed;
width: 100%;
z-index: 1000;
margin: 0;
border-bottom: 5px solid #00a7e9;
}
.header h1 {
flex: 1;
font-size: 20px;
font-weight: 400;
}
.header button {
border: solid 2px #fff;
background-color: #000;
color: #fff;
margin-left: 20px;
height: 30px;
}
.header button:hover {
background-color: #fff;
color: #000;
}
button, .firmware {
height: 25px;
font-size: 16px;
border-radius: 15px;
padding-left: 20px;
padding-right: 20px;
border-width: 2px;
}
body {
font-family: proxima-nova, sans-serif;
font-style: normal;
font-weight: 400;
margin: 0;
}
p {
margin: 0;
}
input, select, button, label {
font-weight: 600;
outline: none;
}
div.left {
float: left;
display: flex;
align-items: center;
}
div.right {
float: right;
display: flex;
align-items: center;
}
div.clear {
clear: both;
}
.Adafruit-Logo {
width: 115px;
height: 40px;
object-fit: contain;
}
.main {
overflow-x: hidden;
overflow-y: auto;
padding-top: 80px;
}
.hidden {
display: none;
}
.notSupported {
padding: 1em;
margin-top: 1em;
margin-bottom: 1em;
}
.subheader {
height: 100px;
line-height: 100px;
padding-left: 70px;
padding-right: 70px;
}
.subheader .title {
font-size: 36px;
font-weight: 500;
}
#app.connected #commands {
height: 200px;
}
#app.connected #log {
height: calc(100vh - 530px);
}
#app #commands {
height: 0;
}
#app #log {
height: calc(100vh - 330px);
}
#commands, #log {
transition: height 0.5s;
}
#commands {
position: relative;
margin: 0 auto;
overflow-y: auto;
padding: 0 60px;
}
#log {
max-width: 100%;
font-family: pt-mono, monospace;
font-style: normal;
font-weight: 400;
font-size: 16px;
overflow-x: hidden;
overflow-x: auto;
transition : color 0.1s linear;
padding: 0 50px;
border: 20px solid #000;
-ms-overflow-style: none;
scrollbar-width: none;
background-color: #000;
color: #cecece;
}
#log::-webkit-scrollbar {
display: none;
}
.footer {
height: 45px;
line-height: 45px;
padding-left: 70px;
padding-right: 70px;
}
.footer button {
font-size: 14px;
margin: 0 10px;
}
.remix {
display: flex;
justify-content: center;
height: 60px;
position: relative;
}
.remix button {
position: absolute;
bottom: 11px;
}
#templates {
display: none;
}
/* On/Off Switch Widget */
.onoffswitch {
display: inline-block;
position: relative;
width: 50px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
margin-left: 10px;
}
.onoffswitch-checkbox {
display: none;
}
.onoffswitch-label {
display: block;
overflow: hidden;
cursor: pointer;
border: 1px solid #900;
border-radius: 15px;
transition: border 0.3s ease-in 0s;
}
.onoffswitch-inner {
display: block;
width: 200%;
margin-left: -100%;
transition: margin 0.3s ease-in 0s;
}
.onoffswitch-inner:before,
.onoffswitch-inner:after {
display: block;
float: left;
width: 50%;
height: 25px;
padding: 0;
line-height: 25px;
font-size: 14px;
color: white;
font-family: proxima-nova, sans-serif;
font-style: normal;
font-weight: 600;
box-sizing: border-box;
}
.onoffswitch-inner:before {
content: "on";
padding-left: 6px;
background-color: #8ec641;
color: #fff;
}
.onoffswitch-inner:after {
content: "off";
padding-right: 6px;
background-color: #c64141;
color: #fff;
text-align: right;
}
.onoffswitch-switch {
display: block;
width: 19px;
margin: 3px;
background: #fff;
position: absolute;
top: 0;
bottom: 0;
right: 23px;
border: 1px solid #900;
border-radius: 15px;
transition: all 0.3s ease-in 0s;
}
.onoffswitch-checkbox:checked + .onoffswitch-label {
border-color: #71ae1e;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
margin-left: 0;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
right: 0px;
border-color: #67ac38;
}
.footer .onoffswitch {
margin-right: 20px;
}
#fpsCounter {
margin-right: 10px;
}
#butClear {
margin-top: 10px;
}
#commands {
min-width: 600px;
justify-content: center;
position: relative;
align-items: center;
}
#commands .field {
width: 400px;
display: flex;
align-items: center;
margin: 5px auto;
justify-content: flex-start;
}
#commands .field label {
white-space: nowrap;
}
#commands .field span {
width: 200px;
display: inline-flex;
justify-content: flex-end;
}
.firmware > input {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
.firmware {
border-style: solid;
margin-left: 20px;
margin-right: 20px;
display: flex;
align-items: center;
cursor: pointer;
}
.firmware > svg {
margin-right: 10px;
}
.debug-function {
color: #8ec641;
}
.error-message {
color: #c64141;
}
.timestamp {
color: #8ec641;
}
.progress-bar {
width: 100%;
height: 24px;
border-style: solid;
border-width: 2px;
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;
}

8
files/secrets.json Normal file
View file

@ -0,0 +1,8 @@
{
"io_username": "YOUR_IO_USERNAME_HERE",
"io_key": "YOUR_IO_KEY_HERE",
"network_type_wifi_native": {
"network_ssid": "YOUR_WIFI_SSID_HERE",
"network_password": "YOUR_WIFI_PASS_HERE"
}
}

114
index.html Normal file
View file

@ -0,0 +1,114 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Install Adafruit.io WipperSnapper for ESP32</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
// Redirect to HTTPS if HTTP is requested.
if (window.location.protocol === 'http:') {
window.location.href = 'https:' + window.location.href.substring(5);
}
</script>
<!-- import the web page's stylesheets along with the themes -->
<link rel="stylesheet" href="https://use.typekit.net/qtk5kiq.css">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/light.css" id="light" class="alternate" disabled>
<link rel="stylesheet" href="css/dark.css" id="dark" class="alternate" disabled>
<!-- import the webpage's javascript file -->
<script src="https://adafruit.github.io/Adafruit_WebSerial_ESPTool/js/utilities.js" defer></script>
<script src="https://adafruit.github.io/Adafruit_WebSerial_ESPTool/js/esptool.js" defer></script>
<script src="js/littlefs.js" defer></script>
<script src="js/script.js" defer></script>
</head>
<body>
<header class="header">
<div class="left">
<img src="https://adafruit.github.io/Adafruit_WebSerial_ESPTool/assets/adafruit-logo.svg" class="Adafruit-Logo">
</div>
<div class="right">
<select id="baudRate"></select>
<button id="butConnect" type="button">Connect</button>
</div>
</header>
<main class="main">
<div id="notSupported" class="notSupported">
Sorry, <b>Web Serial</b> is not supported on this device, make sure you're
running Chrome 78 or later and have enabled the
<code>#enable-experimental-web-platform-features</code> flag in
<code>chrome://flags</code>
</div>
<div class="subheader">
<div class="title left">
Install Adafruit.io WipperSnapper for ESP32
</div>
<div class="right">
<label for="darkmode">
Dark Mode
</label>
<div class="onoffswitch">
<input type="checkbox" name="darkmode" class="onoffswitch-checkbox" id="darkmode">
<label class="onoffswitch-label" for="darkmode">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</div>
<div id="app" class="connected">
<div id="commands">
<div class="field">
<label><span>SSID:</span>
<input id="network_type_wifi_native.network_ssid" class="partition-data" type="text" placeholder="WiFi SSID" value="" />
</label>
</div>
<div class="field">
<label><span>Password:</span>
<input id="network_type_wifi_native.network_password" class="partition-data" type="text" placeholder="WiFi Password" value="" />
</label>
</div>
<div class="field">
<label><span>Adafruit IO Username:</span>
<input id="io_username" class="partition-data" type="text" placeholder="AIO Username" value="" />
</label>
</div>
<div class="field">
<label><span>Adafruit IO Password:</span>
<input id="io_key" class="partition-data" type="text" placeholder="AIO Password" value="" />
</label>
</div>
<div class="buttons">
<div id="stepname" class="hidden"></div>
</div>
<div class="buttons">
<div id="progressBar" class="progress-bar hidden"><div></div></div>
</div>
<div class="buttons">
<button id="butProgram" type="button" disabled="disabled">Install WipperSnapper</button>
<button id="butProgramNvm" type="button" disabled="disabled">Update Credentials ONLY</button>
</div>
</div>
<div id="log"></div>
</div>
</main>
<footer class="footer">
<div class="left">
<label for="autoscroll">Autoscroll</label>
<div class="onoffswitch">
<input type="checkbox" name="autoscroll" class="onoffswitch-checkbox" id="autoscroll">
<label class="onoffswitch-label" for="autoscroll">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
<div class="right">
<span id="fpsCounter"></span>
<button id="butClear" type="button">Clear Text</button>
</div>
</footer>
</body>
</html>

3465
js/littlefs.js Normal file

File diff suppressed because it is too large Load diff

628
js/script.js Normal file
View file

@ -0,0 +1,628 @@
// Note: the code will still work without this line, but without it you
// will see an error in the editor
/* global EspLoader, ESP_ROM_BAUD, port, reader, inputBuffer, generate */
'use strict';
const ESP8266_SETTINGS = {
"offset": 0x200000,
"imagesize": 65536,
"blocksize": 8192
}
const ESP32_SETTINGS = {
"offset": 0x290000,
"imagesize": 94208,
"blocksize": 4096,
}
// C3 is not tested (or currently used)
const ESP32_C3_SETTINGS = {
"offset": 0x290000,
"imagesize": 94208,
"blocksize": 4096,
}
const DO_DOWNLOAD = false
let espTool;
let isConnected = false;
const baudRates = [115200];
const flashSizes = {
"512KB": 0x00,
"256KB": 0x10,
"1MB": 0x20,
"2MB": 0x30,
"4MB": 0x40,
"2MB-c1": 0x50,
"4MB-c1": 0x60,
"8MB": 0x80,
"16MB": 0x90,
};
const secretsFilename = "secrets.json";
const binFolder = "bin/";
const structure = {
0xe000: "boot_app0.bin",
0x1000: "Wippersnapper_demo.ino.bootloader.bin",
0x10000: "Wippersnapper_demo.ino.bin",
0x8000: "Wippersnapper_demo.ino.partitions.bin",
}
const stage_erase_all = 0x01;
const stage_flash_structure = 0x02;
const stage_flash_nvm = 0x03;
const full_program = [stage_erase_all, stage_flash_structure, stage_flash_nvm];
const nvm_only_program = [stage_flash_nvm];
const bufferSize = 512;
const colors = ['#00a7e9', '#f89521', '#be1e2d'];
const measurementPeriodId = '0001';
const maxLogLength = 100;
const log = document.getElementById('log');
const butConnect = document.getElementById('butConnect');
const baudRate = document.getElementById('baudRate');
const butClear = document.getElementById('butClear');
const butProgram = document.getElementById('butProgram');
const butProgramNvm = document.getElementById('butProgramNvm');
const autoscroll = document.getElementById('autoscroll');
const lightSS = document.getElementById('light');
const darkSS = document.getElementById('dark');
const darkMode = document.getElementById('darkmode');
const partitionData = document.querySelectorAll(".field input.partition-data");
const progress = document.getElementById('progressBar');
const stepname = document.getElementById('stepname');
const appDiv = document.getElementById('app');
const disableWhileBusy = [partitionData, butProgram, butProgramNvm, baudRate];
let colorIndex = 0;
let activePanels = [];
let bytesReceived = 0;
let currentBoard;
let buttonState = 0;
document.addEventListener('DOMContentLoaded', () => {
let debug = false;
var getParams = {}
location.search.substr(1).split("&").forEach(function(item) {getParams[item.split("=")[0]] = item.split("=")[1]})
if (getParams["debug"] !== undefined) {
debug = getParams["debug"] == "1" || getParams["debug"].toLowerCase() == "true";
}
espTool = new EspLoader({
updateProgress: updateProgress,
logMsg: logMsg,
debugMsg: debugMsg,
debug: debug})
butConnect.addEventListener('click', () => {
clickConnect().catch(async (e) => {
errorMsg(e.message);
disconnect();
});
});
butClear.addEventListener('click', clickClear);
butProgram.addEventListener('click', clickProgram);
butProgramNvm.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);
darkMode.addEventListener('click', clickDarkMode);
window.addEventListener('error', function(event) {
console.log("Got an uncaught error: ", event.error)
});
if ('serial' in navigator) {
const notSupported = document.getElementById('notSupported');
notSupported.classList.add('hidden');
}
initBaudRate();
loadAllSettings();
updateTheme();
logMsg("Adafruit WebSerial ESPTool loaded.");
checkProgrammable();
});
/**
* @name connect
* Opens a Web Serial connection to a micro:bit and sets up the input and
* output stream.
*/
async function connect() {
logMsg("Connecting...")
await espTool.connect()
readLoop().catch((error) => {
toggleUIConnected(false);
});
}
function initBaudRate() {
for (let rate of baudRates) {
var option = document.createElement("option");
option.text = rate + " Baud";
option.value = rate;
baudRate.add(option);
}
}
let lastPercent = 0;
function updateProgress(part, percentage) {
if (percentage != lastPercent) {
logMsg(percentage + "%...");
lastPercent = percentage;
}
let progressBar = progress.querySelector("div");
progressBar.style.width = percentage + "%";
}
/**
* @name disconnect
* Closes the Web Serial connection.
*/
async function disconnect() {
toggleUIToolbar(false);
await espTool.disconnect()
toggleUIConnected(false);
}
/**
* @name readLoop
* Reads data from the input stream and places it in the inputBuffer
*/
async function readLoop() {
reader = port.readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}
inputBuffer = inputBuffer.concat(Array.from(value));
}
}
function logMsg(text) {
log.innerHTML += text+ "<br>";
// Remove old log content
if (log.textContent.split("\n").length > maxLogLength + 1) {
let logLines = log.innerHTML.replace(/(\n)/gm, "").split("<br>");
log.innerHTML = logLines.splice(-maxLogLength).join("<br>\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 = '<span class="debug-function">[' + top.func + ":" + top.pos + ']</span> ';
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 => espTool.toHex(value)).join(", ") + "]");
} else if (typeof arg == "object" && (arg instanceof Uint8Array)) {
logMsg(prefix + "[" + Array.from(arg).map(value => espTool.toHex(value)).join(", ") + "]");
} else {
logMsg(prefix + "Unhandled type of argument:" + typeof arg);
console.log(arg);
}
prefix = ""; // Only show for first argument
}
}
function errorMsg(text) {
logMsg('<span class="error-message">Error:</span> ' + text);
console.log(text);
}
function formatMacAddr(macAddr) {
return macAddr.map(value => value.toString(16).toUpperCase().padStart(2, "0")).join(":");
}
/**
* @name updateTheme
* Sets the theme to Adafruit (dark) mode. Can be refactored later for more themes
*/
function updateTheme() {
// Disable all themes
document
.querySelectorAll('link[rel=stylesheet].alternate')
.forEach((styleSheet) => {
enableStyleSheet(styleSheet, false);
});
if (darkMode.checked) {
enableStyleSheet(darkSS, true);
} else {
enableStyleSheet(lightSS, true);
}
}
function enableStyleSheet(node, enabled) {
node.disabled = !enabled;
}
/**
* @name reset
* Reset the Panels, Log, and associated data
*/
async function reset() {
bytesReceived = 0;
// Clear the log
log.innerHTML = "";
}
/**
* @name clickConnect
* Click handler for the connect/disconnect button.
*/
async function clickConnect() {
if (espTool.connected()) {
await disconnect();
return;
}
await connect();
toggleUIConnected(true);
try {
if (await espTool.sync()) {
toggleUIToolbar(true);
appDiv.classList.add("connected");
let baud = parseInt(baudRate.value);
logMsg("Connected to " + await espTool.chipName());
logMsg("MAC Address: " + formatMacAddr(espTool.macAddr()));
espTool = await espTool.runStub();
if (baud != ESP_ROM_BAUD) {
if (await espTool.chipType() == ESP32) {
logMsg("WARNING: ESP32 is having issues working at speeds faster than 115200. Continuing at 115200 for now...")
} else {
await changeBaudRate(baud);
}
}
}
} catch(e) {
errorMsg(e);
await disconnect();
return;
}
}
/**
* @name changeBaudRate
* Change handler for the Baud Rate selector.
*/
async function changeBaudRate() {
saveSetting('baudrate', baudRate.value);
if (isConnected) {
let baud = parseInt(baudRate.value);
if (baudRates.includes(baud)) {
await espTool.setBaudrate(baud);
}
}
}
/**
* @name clickAutoscroll
* Change handler for the Autoscroll checkbox.
*/
async function clickAutoscroll() {
saveSetting('autoscroll', autoscroll.checked);
}
/**
* @name clickDarkMode
* Change handler for the Dark Mode checkbox.
*/
async function clickDarkMode() {
updateTheme();
saveSetting('darkmode', darkMode.checked);
}
/**
* @name clickProgram
* Click handler for the program button.
*/
async function clickProgram() {
await programScript(full_program);
}
/**
* @name clickProgramNvm
* Click handler for the program button.
*/
async function clickProgramNvm() {
await programScript(nvm_only_program);
}
async function populateSecretsFile(path) {
let response = await fetch(path);
let contents = await response.json();
// Get the secrets data
for (let field of getValidFields()) {
updateObject(contents, partitionData[field].id, partitionData[field].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;
}
async function programScript(stages) {
let params = {
'files': [{
'filename': secretsFilename,
'callback': populateSecretsFile,
}],
'rootFolder': "files",
'fileSystemSize': 0,
'blockSize': 0,
}
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 espTool.eraseFlash();
},
params: {},
})
} else if (stages[i] == stage_flash_structure) {
for (const [offset, filename] of Object.entries(structure)) {
steps.push({
name: "Flashing " + filename,
func: async function(params) {
let firmware = await getFirmware(params["filename"]);
await espTool.flashData(firmware, params["offset"], 0);
},
params: {
filename: filename,
offset: offset,
}
})
}
} else if (stages[i] == stage_flash_nvm) {
steps.push({
name: "Generating and Flashing LittleFS Partition",
func: async function(params) {
let chipType = await espTool.chipType();
let offset;
if (chipType == ESP8266) {
logMsg("Using ESP8266 Settings...");
params.params.fileSystemSize = ESP8266_SETTINGS.imagesize;
params.params.blockSize = ESP8266_SETTINGS.blocksize;
offset = ESP8266_SETTINGS.offset;
} else if (chipType == ESP32) {
logMsg("Using ESP32 Settings...");
params.params.fileSystemSize = ESP32_SETTINGS.imagesize;
params.params.blockSize = ESP32_SETTINGS.blocksize;
offset = ESP32_SETTINGS.offset;
} else {
errorMsg("Unsupported Chip!");
return;
}
let fileSystemImage = await generate(params.params);
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 {
await espTool.flashData(new Uint8Array(fileSystemImage).buffer, offset, 0);
}
},
params: {
params: params,
}
})
}
}
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");
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();
disconnect();
logMsg("To run the new firmware, please reset your device.")
}
function getValidFields() {
// 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 validFields = [];
for (let i=0; i<4; i++) {
//let pd = parseInt(partitionData[i].value, 16);
if (partitionData[i].value.length > 0) {
validFields.push(i);
}
}
return validFields;
}
/**
* @name checkProgrammable
* Check if the conditions to program the device are sufficient
*/
async function checkProgrammable() {
butProgramNvm.disabled = getValidFields().length < 4;
butProgram.disabled = getValidFields().length < 4;
}
/**
* @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) + "&hellip;";
} else {
label.innerHTML = filename;
}
icon.classList.add("hidden");
} else {
label.innerHTML = "Choose a file&hellip;";
icon.classList.remove("hidden");
}
await checkProgrammable();
}
/**
* @name clickClear
* Click handler for the clear button.
*/
async function clickClear() {
reset();
}
function convertJSON(chunk) {
try {
let jsonObj = JSON.parse(chunk);
return jsonObj;
} catch (e) {
return chunk;
}
}
function toggleUIToolbar(show) {
isConnected = 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 = 'Disconnect';
} else {
toggleUIToolbar(false);
}
butConnect.textContent = lbl;
}
function loadAllSettings() {
// Load all saved settings or defaults
autoscroll.checked = loadSetting('autoscroll', true);
baudRate.value = loadSetting('baudrate', baudRates[0]);
darkMode.checked = loadSetting('darkmode', false);
}
function loadSetting(setting, defaultValue) {
let value = JSON.parse(window.localStorage.getItem(setting));
if (value == null) {
return defaultValue;
}
return value;
}
function saveSetting(setting, value) {
window.localStorage.setItem(setting, JSON.stringify(value));
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getFirmware(filename) {
let response = await fetch(binFolder + filename);
return await response.arrayBuffer();
}

10
license.md Normal file
View file

@ -0,0 +1,10 @@
MIT License
Copyright (c) 2020 Melissa LeBlanc-Williams for Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
stubs/esp32.json Normal file
View file

@ -0,0 +1 @@
{"text": "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", "text_start": 1074520064, "entry": 1074521516, "data": "CMD8Pw==", "data_start": 1073605544}

1
stubs/esp32c3.json Normal file
View file

@ -0,0 +1 @@
{"text": "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", "text_start": 1077411840, "entry": 1077413488, "data": "DEDIPw==", "data_start": 1070164904}

1
stubs/esp32h2.json Normal file
View file

@ -0,0 +1 @@
{"text": "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", "text_start": 1077411840, "entry": 1077413328, "data": "DEDIPw==", "data_start": 1070164904}

1
stubs/esp32s2.json Normal file

File diff suppressed because one or more lines are too long

1
stubs/esp32s3.json Normal file

File diff suppressed because one or more lines are too long

1
stubs/esp8266.json Normal file

File diff suppressed because one or more lines are too long

7
wsPartitions.csv Normal file
View file

@ -0,0 +1,7 @@
# Wippersnapper ESP32 Non-Volatile Storage (NVM) Partition Template,,,
key,type,encoding,value
wsNamespace,namespace,,
wsNetSSID,data,string,YOURSSID
wsNetPass,data,string,YOURSSIDPASS
wsAIOUser,data,string,YOURAIOUSER
wsAIOKey,data,string,YOURAIOPASS
1 # Wippersnapper ESP32 Non-Volatile Storage (NVM) Partition Template
2 key type encoding value
3 wsNamespace namespace
4 wsNetSSID data string YOURSSID
5 wsNetPass data string YOURSSIDPASS
6 wsAIOUser data string YOURAIOUSER
7 wsAIOKey data string YOURAIOPASS